Dominik Kolesar
Dominik Kolesar

Reputation: 130

How can I bind class fields to elements inside custom templates ? (WPF)

How can I bind class fields to elements inside a custom ListBoxItem template?? This sample code is lacking some aspects like class definition, ListBox name etc., I just struggle with the binding process.

<ListBox>
    <ListBox.Resources>
        <Style TargetType="ListBoxItem">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>

                       <!-- how to bind class fields to these elements -->

                        <StackPanel Orientation="Horizontal">
                            <Label></Label>
                            <Image></Image>
                        </StackPanel>

                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.Resources>

    <!-- adding items dynamically -->

</ListBox>

C# code should look like:

ListBoxItem listBoxItem = new ListBoxItem();
listBoxItem.Content = new MyClass(){ Name="MyName", Image="ImagePath"}
... append to ListBox ... 

Upvotes: 0

Views: 117

Answers (2)

XAMlMAX
XAMlMAX

Reputation: 2363

When developing in WPF MvvM is being encouraged to promote separation of concerns. To do so one would implement a View Model with Properties that then would be bound to the view. When you want the UI (view) to be aware of the changes to the data that View Model provides one has to implement the INotifyPropertyChanged interface, like so:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;    

namespace ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public BaseViewModel()
        {
             //ctor
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            UIThread( () =>
            {
                //make sure the event is raised on the main thread
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            });
        }

        Dispatcher _dispacther;

        protected void UIThread(Action action)
        {
            if (_dispacther == null)
            {
                _dispacther = Dispatcher.CurrentDispatcher;
            }
            _dispacther.Invoke(action);
        }
    }
}  

Your Hello class would derive from the BaseViewModel and provide OnPropertyChanged(); in the property like this:

    private string name;

    public string Name
    {
        get { return name; }
        set { name= value; OnPropertyChanged(); }
    }

Now when you change a name of the selected Item in the ListBox it will be reflected in the UI. Try it! This implementation ensures that the event is raised on main thread, so you don't need to do it in when invoking the event. Also [CallerMemberName] will populate the name of the property that you invoke!

Upvotes: 1

Dominik Kolesar
Dominik Kolesar

Reputation: 130

Figured it out. C# class:

        public class Hello
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public string ImagePath { get; set; }
    }

C# dynamic adding:

ListBoxItem lbi = new ListBoxItem();
lbi.Content = new Hello() { Name = "hello man", Description="I am superman.", ImagePath="Images/myimage.png"};
menu.Items.Add(lbi);

XAML:

            <ListBox Name="menu" ItemsSource="{Binding}">

                <ListBox.Resources>

                    <Style TargetType="ListBoxItem">
                        <Setter Property="ContentTemplate">
                            <Setter.Value>

                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Label FontWeight="Bold" Content="{Binding Name}"></Label>
                                        <Label Content="{Binding Description}"></Label>
                                        <Image Source="{Binding ImagePath}" Width="30" Height="30"></Image>
                                    </StackPanel>

                                </DataTemplate>

                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.Resources>


            </ListBox>

Upvotes: 0

Related Questions