Search code examples
.netwpfdata-binding

WPF TreeViewItem template (icon+text)


I have this TreeView where every item contains an icon + a text:

    <TreeView x:Name="myTreeView" Height="153" VerticalAlignment="Top" Width="215">
        <TreeView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Image Source="{Binding IconSource}"/>
                    <TextBlock Text="{Binding Name}"/>
                </StackPanel>
            </DataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

this is working (showing every item as icon+text) if I use this code-behind:

ObservableCollection<NameIconPair> pairs = new ObservableCollection<NameIconPair>();
pairs.Add(new NameIconPair() { Name = "item 1", IconSource = src });
pairs.Add(new NameIconPair() { Name = "item 2", IconSource = src });
pairs.Add(new NameIconPair() { Name = "item 3", IconSource = src });
myTreeView.ItemsSource = pairs;

public class NameIconPair {
    public string Name { get; set; }
    public BitmapSource IconSource { get; set; }
}

but it does not work if instead of creating an observableCollection and using .ItemsSource, I create every TreeViewItem and add it to the TreeView:

   var pair1 = new NameIconPair() { Name = "item 1", IconSource = src };
   var pair2 = new NameIconPair() { Name = "item 2", IconSource = src };
   var pair3 = new NameIconPair() { Name = "item 3", IconSource = src };

   TreeViewItem node1 = new TreeViewItem { Header = pair, IsExpanded = true };
   TreeViewItem node2 = new TreeViewItem { Header = pair, IsExpanded = true };
   TreeViewItem node3 = new TreeViewItem { Header = pair, IsExpanded = true };
   
   node1.Items.Add(node2);
   node1.Items.Add(node3);
   myTreeView.Items.Add(node1);

How can I make this work?


Solution

  • Add a Children property to your NameIconPair class to make it hierarchical:

    public class NameIconPair
    {
        public string Name { get; set; }
        public BitmapSource IconSource { get; set; }
        public List<NameIconPair> Children { get; } = new List<NameIconPair>();
    }
    

    ...and replace the DataTemplate with a HierarchicalDataTemplate that binds to the Children property:

    <TreeView x:Name="myTreeView" Height="153" VerticalAlignment="Top" Width="215">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="{Binding IconSource}"/>
                    <TextBlock Text="{Binding Name}"/>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    

    You can build your tree without using TreeViewItem elements in the view model:

    var pair1 = new NameIconPair() { Name = "item 1", IconSource = src };
    var pair2 = new NameIconPair() { Name = "item 2", IconSource = src };
    var pair3 = new NameIconPair() { Name = "item 3", IconSource = src };
    
    var node1 = new NameIconPair { Name = "parent" };
    node1.Children.Add(pair1);
    node1.Children.Add(pair2);
    node1.Children.Add(pair3);
    
    myTreeView.Items.Add(node1); // or add to source collection...