Search code examples
c#uwpwinui-3winuiwinui-xaml

How do I make a TreeView with different parent and child nodes?


I'm developing an application on Win UI and I need to make a TreeView with different parent and child nodes. The parent nodes are objects of the "Silos" class.

public class Silos : BindableBase
{
  private ObservableCollection<Wire> wiresObservable;

  public ObservableCollection<Wire> WiresObservable
  {
      get => wiresObservable; set=>SetProperty(ref this.wiresObservable, value);
  }
}

Each "Silos" object has child "Wire" objects.

public class Wire : BindableBase
{
    private int id; // уникальный идентификатор подвески
    private int number;
}

I am able to output a list of Silos, but it does not output Wire objects. How do I do this?

The code that outputs the Silos list.

<TreeView x:Name="TreeSilos" SelectionMode="Multiple"  ItemsSource="{x:Bind ViewModel.Siloses}">
    <TreeView.ItemTemplate>
        <DataTemplate x:DataType="local:Silos">
            <TreeViewItem  ItemsSource="{x:Bind Wires, Mode=OneWay}" Content="{x:Bind Name}" >
            </TreeViewItem>
        </DataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Result

I looked at the examples on the Microsoft website. https://learn.microsoft.com/ru-ru/windows/apps/design/controls/tree-view


Solution

  • You can create a custom DataTemplateSelector and select a DataTemplate for each type.

    Let me show you an example for a Parent type and a Child type:

    Parent.cs

    public partial class Parent(string name) : ObservableObject
    {
        [ObservableProperty]
        private string _name = name;
    
        [ObservableProperty]
        private ObservableCollection<Child> _children = [];
    }
    

    Child.cs

    public partial class Child(string name) : ObservableObject
    {
        [ObservableProperty]
        private string _name = name;
    }
    

    TreeViewItemTemplateSelector.cs

    public class TreeViewItemTemplateSelector : DataTemplateSelector
    {
        public DataTemplate ParentItemTemplate { get; set; } = new();
    
        public DataTemplate ChildItemTemplate { get; set; } = new();
    
        protected override DataTemplate SelectTemplateCore(object item)
        {
            return item switch
            {
                Parent => ParentItemTemplate,
                Child => ChildItemTemplate,
                _ => base.SelectTemplateCore(item)
            };
        }
    }
    

    ShellViewModel.cs

    public partial class ShellViewModel : ObservableObject
    {
    
        [ObservableProperty]
        private ObservableCollection<Parent> _parents = [];
    
        public ShellViewModel()
        {
            var parent1 = new Parent("Parent 1");
            parent1.Children.Add(new Child("Child 1"));
    
            var parent2 = new Parent("Parent 2");
            parent2.Children.Add(new Child("Child 1"));
            parent2.Children.Add(new Child("Child 2"));
    
            var parent3 = new Parent("Parent 3");
            parent3.Children.Add(new Child("Child 1"));
            parent3.Children.Add(new Child("Child 2"));
            parent3.Children.Add(new Child("Child 3"));
    
            Parents.Add(parent1);
            Parents.Add(parent2);
            Parents.Add(parent3);
        }
    }
    

    Shell.xaml.cs

    public sealed partial class Shell : Page
    {
        public Shell()
        {
            InitializeComponent();
        }
    
        public ShellViewModel ViewModel { get; } = new();
    }
    

    Shell.xaml

    <Page
        x:Class="WinUIDemoApp.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="using:WinUIDemoApp"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        mc:Ignorable="d">
    
        <Page.Resources>
            <local:TreeViewItemTemplateSelector x:Name="TreeViewItemTemplateSelector">
                <local:TreeViewItemTemplateSelector.ParentItemTemplate>
                    <DataTemplate x:DataType="local:Parent">
                        <TreeViewItem
                            Content="{x:Bind Name}"
                            ItemsSource="{x:Bind Children}" />
                    </DataTemplate>
                </local:TreeViewItemTemplateSelector.ParentItemTemplate>
                <local:TreeViewItemTemplateSelector.ChildItemTemplate>
                    <DataTemplate x:DataType="local:Child">
                        <TreeViewItem Content="{x:Bind Name}" />
                    </DataTemplate>
                </local:TreeViewItemTemplateSelector.ChildItemTemplate>
            </local:TreeViewItemTemplateSelector>
        </Page.Resources>
    
        <TreeView
            ItemTemplateSelector="{StaticResource TreeViewItemTemplateSelector}"
            ItemsSource="{x:Bind ViewModel.Parents, Mode=OneWay}" />
    
    </Page>
    

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