Search code examples
c#mvvmwinui-3winuiwinui-xaml

Populate more nested WinUI TreeView


I want to show View Model on my UI using treeview, but I could not find a way to go more than one node deep The view should show the Persons and their Adress and their Adress details


using System.Collections.ObjectModel;
using System.ComponentModel;

public class Person : INotifyPropertyChanged
{
    public string Name { get; set; }
    public Address Address { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class Address : INotifyPropertyChanged
{
    public string Street { get; set; }
    public Postcode Postcode { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class Postcode : INotifyPropertyChanged
{
    public string Code { get; set; }
    public string Name { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
}

Tried nesting them inside each other with Datatemplates but no success


Solution

  • You can use the DataTemplateSelector for this. For example:

    public partial class Person(string name, Address address) : ObservableObject
    {
        [ObservableProperty]
        private string _name = name;
    
        [ObservableProperty]
        private Address _address = address;
    }
    
    
    public partial class Address(string street, Postcode postcode) : ObservableObject
    {
        [ObservableProperty]
        private string _street = street;
    
        [ObservableProperty]
        private Postcode _postcode = postcode;
    }
    
    
    public partial class Postcode(string code, string name) : ObservableObject
    {
        [ObservableProperty]
        private string _code = code;
    
        [ObservableProperty]
        private string _name = name;
    }
    
    public class TreeViewItemTemplateSelector : DataTemplateSelector
    {
        public DataTemplate PersonTemplate { get; set; } = new();
    
        public DataTemplate AddressTemplate { get; set; } = new();
    
        public DataTemplate PostcodeTemplate { get; set; } = new();
    
        protected override DataTemplate SelectTemplateCore(object item)
        {
            return item switch
            {
                Person => PersonTemplate,
                Address => AddressTemplate,
                Postcode => PostcodeTemplate,
                _ => base.SelectTemplateCore(item)
            };
        }
    }
    
    public class SingleItemToCollectionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return new[] { value };
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
    
    <Page.Resources>
        <local:SingleItemToCollectionConverter x:Key="SingleItemToCollectionConverter" />
    
        <local:TreeViewItemTemplateSelector x:Key="TreeViewItemTemplateSelector">
    
            <local:TreeViewItemTemplateSelector.PersonTemplate>
                <DataTemplate x:DataType="local:Person">
                    <TreeViewItem
                        Content="{x:Bind Name, Mode=OneWay}"
                        ItemsSource="{x:Bind Address, Mode=OneWay, Converter={StaticResource SingleItemToCollectionConverter}}" />
                </DataTemplate>
            </local:TreeViewItemTemplateSelector.PersonTemplate>
    
            <local:TreeViewItemTemplateSelector.AddressTemplate>
                <DataTemplate x:DataType="local:Address">
                    <TreeViewItem
                        Content="{x:Bind Street, Mode=OneWay}"
                        ItemsSource="{x:Bind Postcode, Mode=OneWay, Converter={StaticResource SingleItemToCollectionConverter}}" />
                </DataTemplate>
            </local:TreeViewItemTemplateSelector.AddressTemplate>
    
            <local:TreeViewItemTemplateSelector.PostcodeTemplate>
                <DataTemplate x:DataType="local:Postcode">
                    <TreeViewItem
                        Content="{x:Bind Name, Mode=OneWay}"
                        ItemsSource="{x:Bind Code, Mode=OneWay, Converter={StaticResource SingleItemToCollectionConverter}}" />
                </DataTemplate>
            </local:TreeViewItemTemplateSelector.PostcodeTemplate>
    
        </local:TreeViewItemTemplateSelector>
    </Page.Resources>
    
    <TreeView
        ItemTemplateSelector="{StaticResource TreeViewItemTemplateSelector}"
        ItemsSource="{x:Bind ViewModel.People, Mode=OneWay}" />
    

    ObservableObject and ObservableProperty come from the CommunityToolkit.Mvvm NuGet package.