How to bind the DataTemplate within an ItemsControl

16,145

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

Share:
16,145
Alan Wayne
Author by

Alan Wayne

Updated on June 04, 2022

Comments

  • Alan Wayne
    Alan Wayne almost 2 years

    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");
                }
            }
        }