2
votes

I do not understand the DataTemplate within the ItemTemplate. I have an ObservableCollection"StringViewModel" Transcription that provides the ItemsSource for an ItemsControl. Populating the Transcription collection with StringViewModel correctly displays these strings.

At the time of displaying each string, I would like the XAML binding to call MyConverter such that additional code can be run on each item being displayed. (I am not trying to change what is displayed, but only perform some actions based on the position of what is displayed).

In the following code, MyConverter is never called.

What is the best way to call MyConverter on each item presented in the ItemsControl?

Any help is appreciated.

C#

public class StringViewModel : FrameworkElement {...}

private ObservableCollection<StringViewModel> transcription = new ObservableCollection<StringViewModel>();
    public ObservableCollection<StringViewModel> Transcription
    {
        get
        {
            return transcription;
        }
        set
        {
            transcription = value;
        }
    }

XAML

<ItemsControl 
                ItemsSource="{Binding Transcription}"
                >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas 
                            Background="Transparent"
                             Width="{x:Static h:Constants.widthCanvas}" 
                             Height="{x:Static h:Constants.heightCanvas}" 
                             />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel> 
                <ItemsControl.ItemTemplate>
                    <DataTemplate>              <!-- THIS DOES NOT WORK -->

                        <ContentControl Content="{Binding Converter={StaticResource MyConverter}}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

My attempt to change this to:

<ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StringViewModel ft="{Binding Path=., Converter={StaticResource MyConverter}}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>

Results in:

ItemTemplate and ItemTemplateSelector are ignored for items already of the ItemsControl's container type; Type='StringViewModel'

What to do?

The StringViewModel is defined as:

  public class StringViewModel : FrameworkElement, INotifyPropertyChanged
{

    public StringViewModel()
    {
    }

    public StringViewModel(
        Point topleft, 
        string text, 
        double fontsizediu,                         
        SolidColorBrush brush, 
        Func<FormattedText,FormattedText> f,        
        double lineheight)
    {
        this.text = text;
        this.emSize = fontsizediu;                 
        this.color = brush;
        this.topleft = topleft;
        this.lineheight = lineheight;              
        this.f = f;
    }

    protected override void OnRender(DrawingContext dc)
    {

        ft = new FormattedText(
            text, 
            CultureInfo.CurrentCulture, 
            FlowDirection.LeftToRight,
            new Typeface(new FontFamily("Segoe Script"), FontStyles.Italic, FontWeights.Normal, FontStretches.Normal),
            emSize, 
            color);


        ft.TextAlignment = TextAlignment.Left;

        // apply special styles
        ft = f(ft);
        dc.DrawText(ft, topleft);

    }

    private string text;
    private double emSize;
    private SolidColorBrush color;
    private Func<FormattedText, FormattedText> f;

    public Point topleft;
    private double? Lineheight;


    private FormattedText _ft;
    public FormattedText ft
    {
        get
        {
            return _ft;
        }
        set
        {
            if (_ft != value)
            {
                _ft = value;
                OnPropertyChanged("ft");
            }
        }
    }
1
Which result(effect) do you want to realize? You've set ItemsSource for ItemsControl but you've not set something for Canvas and ContentControl.Rang
@Rang Without the ItemsControl.ItemTemplate section, the strings in Transcription display correctly at the correct positions on the canvas. Ultimately, I would like to use the position of each displayed item to perform visual hit testing within the converter. The Transcription collection is a dynamically changing collection of type FormattedText.Alan Wayne
you mean if you add <ItemsControl.ItemTemplate> the Converter was never called? and with out <ItemsControl.ItemTemplate>, it shows Transcription in Canvas correctly? but i write a simple example, it works. see my temporary answer.Rang
@Rang Exactly. Does it matter that Transcription is an ObservableCollection of FormattedText?Alan Wayne

1 Answers

2
votes

Update:

I advice you not realize Render in ViewModel,Actually your StringViewModel should be a StringModel like this : (just contains some properties)

public class StringModel :  INotifyPropertyChanged
{

    public StringModel()
    {
    }

    public StringModel(
        Point topleft,
        string text,
        double fontsizediu,
        SolidColorBrush brush,
        double lineheight)
    {
        this.text = text;
        this.emSize = fontsizediu;
        this.color = brush;
        this.topleft = topleft;
        this.lineheight = lineheight;
    }


    public string text;
    public double emSize;
    public SolidColorBrush color;

    public Point topleft;
    public double? lineheight;


    private FormattedText _ft;
    public FormattedText ft
    {
        get
        {
            return _ft;
        }
        set
        {
            if (_ft != value)
            {
                _ft = value;
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

and then ,realize render in converter:

 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var stringviewmodel = value as StringModel;
        if (stringviewmodel != null)
        {
            stringviewmodel.ft = new FormattedText(
           stringviewmodel.text,
           CultureInfo.CurrentCulture,
           FlowDirection.LeftToRight,
           new Typeface(new FontFamily("Segoe Script"), FontStyles.Italic, FontWeights.Normal, FontStretches.Normal),
           stringviewmodel.emSize,
           stringviewmodel.color);


            stringviewmodel.ft.TextAlignment = TextAlignment.Left;

            // apply special styles

            Image myImage = new Image();

            DrawingVisual drawingVisual = new DrawingVisual();
            DrawingContext dc = drawingVisual.RenderOpen();
            dc.DrawText(stringviewmodel.ft, stringviewmodel.topleft);
            dc.Close();

            RenderTargetBitmap bmp = new RenderTargetBitmap(180, 180, 120, 96, PixelFormats.Pbgra32);
            bmp.Render(drawingVisual);
            myImage.Source = bmp;

            return myImage;
        }
        else
        {
            return null;
        }
    }

here is itemcontrol's code:

 <ItemsControl x:Name="ic" ItemsSource="{Binding LST}" Margin="0,0,143,185">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel 
                        Background="Transparent"
                         />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <ContentControl Content="{Binding Converter={StaticResource myconverter}}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

and Main code is:

 public partial class MainWindow : Window
{
    private List<StringModel> lst;

    public List<StringModel> LST
    {
        get { return lst; }
        set { lst = value; }
    }

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        LST = new List<StringModel>();

        StringModel vm = new StringModel(new Point(20, 20), "123", 14, Brushes.Red, 2);
        LST.Add(vm);

        vm = new StringModel(new Point(20, 20), "456", 16, Brushes.Blue, 3);
        LST.Add(vm);

        vm = new StringModel(new Point(20, 20), "789", 18, Brushes.Green, 4);
        LST.Add(vm);



    }
}

finally, the effect of it:

enter image description here