Search code examples
c#wpflinqobservablecollectioncollectionviewsource

Binding observable collection to a TreeView in WPF


Thanks in advance,

I want to bind an observable collection to a TreeView in WPF such that it should display the each TreeView Node as a CountryType. The TreeView is bound to this collection from code behind.

Here's the code :

public partial class MyUserControl: UserControl
{
    public ObservableCollection<Person> myList { get; set; }

    public IEnumerable<PersonViewModel> PersonViewModelList { get; set; }

     public MyUserControl()
     {
       InitializeComponent();
       PopulateCollection();
       DataContext = this;
     }

     private void PopulateCollection()
        {
            myList = new ObservableCollection<Person>()
            {
                new Person{ Name="Name 1", Age=21, CountryType = CountryList.India},
                new Person{ Name="Name 2", Age=21, CountryType = CountryList.US},
                new Person{ Name="Name 3", Age=24, CountryType = CountryList.Israel},
                new Person{ Name="Name 4", Age=24, CountryType = CountryList.Israel},
                new Person{ Name="Name 5", Age=24, CountryType = CountryList.Russia},
                new Person{ Name="Name 6", Age=24, CountryType = CountryList.India},
                new Person{ Name="Name 7", Age=24, CountryType = CountryList.Russia},
                new Person{ Name="Name 8", Age=24, CountryType = CountryList.Russia},
                new Person{ Name="Name 9", Age=24, CountryType = CountryList.Japan},
                new Person{ Name="Name 10", Age=24, CountryType = CountryList.Japan},
                new Person{ Name="Name 11", Age=24, CountryType = CountryList.India},
                new Person{ Name="Name 12", Age=24, CountryType = CountryList.India},
            };

            PersonViewModelList = myList.GroupBy(x => x.CountryType)
            .Select(g => new PersonViewModel()
            {
                CountryType = g.Key,
                Names = g.Select(x => x.Name).OrderBy(n => n).ToList()
            });          
        }
  }

  public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public CountryList CountryType { get; set; }        
    }

    public class PersonViewModel
    {
        public CountryList CountryType { get; set; }
        public List<string> Names { get; set; }
    }

  public enum CountryList
    {
        India,
        Russia,
        US,
        Israel,
        Japan
    }

UserControl (XAML file)

 <TreeView ItemsSource="{Binding PersonViewModelList}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Path=Names}" DataType="{x:Type local:PersonViewModel}">
                <HierarchicalDataTemplate.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=CountryType}"></TextBlock>
                    </DataTemplate>
                </HierarchicalDataTemplate.ItemTemplate>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

Expected:

enter image description here

I am pretty not sure what change I should do in TreeView to make it work. Thanks again.


Solution

  • Your current Person type is not suited or valid for your requirements. You need to transform your data into an hierarchical view model:

    public class PersonViewModel
    {
        public CountryList CountryType { get; set; }
        public List<string> Names { get; set; }
    }
    

    Just group your current myList by CountryType and bind to the result:

    var transformedList = myList
        .GroupBy(x => x.CountryType)
        .Select(g => new PersonViewModel()
        { 
            CountryType = g.Key, 
            Names = g.Select(x => x.Name).OrderBy(n => n).ToList() 
        });
    

    XAML:

    <TreeView ItemsSource="{Binding PersonViewModelList}">
        <TreeView.Resources>
            <HierarchicalDataTemplate ItemsSource="{Binding Names}" DataType="{x:Type local:PersonViewModel}">
                <TextBlock Text="{Binding CountryType}" />
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>