19

For my treeview I have two different classes that provide the ItemsSource.

public class TreeViewModel : ViewModelBase
{
    public ObservableCollection<NodeViewModel> Items { get; set; }
}

public class NodeViewModel : ViewModelBase
{
    public string Id { get; set; }
    public string Name { get; set; }
    public ObservableCollection<NodeViewModel> Children { get; set; }
}

Now I want my TreeView to display the Items in TreeViewModel and show hierarchical data as provided by the NodeViewModel.

Here is my XAML

<Window x:Class="TreeViewMasterDetails.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TreeViewMasterDetails" 
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TreeView Height="Auto" 
                  HorizontalAlignment="Stretch" 
                  Margin="10" 
                  VerticalAlignment="Stretch" 
                  Width="Auto">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="x:Type local:TreeViewModel" ItemsSource="{Binding Items}">
                    <TextBlock Text="{Binding Path=Name}"></TextBlock>
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate DataType="x:Type local:NodeViewModel" ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Name}"></TextBlock>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</Window>

Have tried to provide Items as the ItemsSource of the TreeView. It does not show the data hierarchically, if displaying anything.

And I tried using the ItemTemplate instead of the TreeView.Resources, too.

What is wrong about it?

Perhaps a problem is the first TextBlock Text Binding? I want to display the Name property of the NodeViewModel in Items there.

Mare Infinitus
  • 8,024
  • 8
  • 64
  • 113

2 Answers2

34

You should only have to declare the HierarchicalDataTemplate for NodeViewModel as this is the only thing showing in the TreeView, and bind the actual ItemSource to the TreeView

<TreeView ItemsSource="{Binding Items}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:NodeViewModel}" ItemsSource="{Binding Children}">
            <TextBlock Text="{Binding Name}"></TextBlock>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

Full Example

Xaml:

<Window x:Class="WpfApplication13.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication13"
        Title="MainWindow" x:Name="UI" Width="343" Height="744.625" >

    <TreeView DataContext="{Binding ElementName=UI, Path=TreeModel}" ItemsSource="{Binding Items}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:NodeViewModel}" ItemsSource="{Binding Children}">
                <TextBlock Text="{Binding Name}"></TextBlock>
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>

</Window>

Code:

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
    }

    public TreeViewModel TreeModel
    {
        get
        {
            return new TreeViewModel
            {
                Items = new ObservableCollection<NodeViewModel>{
                           new NodeViewModel { Name = "Root", Children =  new ObservableCollection<NodeViewModel> {
                              new NodeViewModel { Name = "Level1" ,  Children = new ObservableCollection<NodeViewModel>{ 
                                  new NodeViewModel{ Name = "Level2"}}} } }}
            };
        }
    }
}

public class TreeViewModel
{
    public ObservableCollection<NodeViewModel> Items { get; set; }
}

public class NodeViewModel
{
    public string Id { get; set; }
    public string Name { get; set; }
    public ObservableCollection<NodeViewModel> Children { get; set; }
}

Result:

enter image description here

sa_ddam213
  • 42,848
  • 7
  • 101
  • 110
  • Yes, it works. Was sure that I have tried that before. Thank you so much! – Mare Infinitus Mar 06 '13 at 06:50
  • Hi, thanks for sharing, I used your solution, however it doesn't seem to let me select anyhting and also the arrows on the left are missing. The tree looks frozen. One change I did to you code, I bind my TreeModel from my ViewModel class not from MainWindow. – Georgiana M Sep 05 '16 at 10:26
  • Worked fine for me. Also see https://stackoverflow.com/questions/2251260/ if you want CheckBoxes that propagate instead of TextBlocks. – Chris Aug 18 '17 at 16:23
4

As @sa_ddam213 said, you only need the HierarchicalDataTemplate for NodeViewModel, but the only problem with your code was the missing braces ({ and }) for DataType="x:Type local:TreeViewModel" in your data template definitions (it should be DataType="{x:Type local:TreeViewModel}"). Adding brackets and ItemsSource binding solves the problem:

The additional HierarchicalDataTemplate for TreeViewModel is not used, but it does not harm.

Mohammad Dehghan
  • 17,853
  • 3
  • 55
  • 72
  • You are right, this is really the problem. Strangely enough, it works without the braces when used in `ItemTemplate`, but not in `Resources`. – Mare Infinitus Mar 06 '13 at 12:39
  • 1
    @MareInfinitus It is because when used int `ItemTemplate`, it deos not need to look at the `DataType` property. You are directly saying to the `TreeView` to use the given template for items. So no need for `DataType`. – Mohammad Dehghan Mar 06 '13 at 12:45