TreeView, HierarchicalDataTemplate and recursive Data
Asked Answered
A

2

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.

Arnoldarnoldo answered 6/3, 2013 at 6:17 Comment(4)
I think this is a duplicate of [WPF TreeView HierarchicalDataTemplate - binding to object with multiple child collections][1]. [1]: #1912981Vasty
Yes, I already have seen that. But it does not work here.Arnoldarnoldo
TreeViewModel does not have a Name property but that's probably not the issueVasty
Any luck for doing this on Windows Phone?Brantbrantford
I
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.

Indeed answered 6/3, 2013 at 7:13 Comment(2)
You are right, this is really the problem. Strangely enough, it works without the braces when used in ItemTemplate, but not in Resources.Arnoldarnoldo
@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.Indeed
O
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

Overall answered 6/3, 2013 at 6:47 Comment(3)
Yes, it works. Was sure that I have tried that before. Thank you so much!Arnoldarnoldo
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.Berghoff
Worked fine for me. Also see stackoverflow.com/questions/2251260 if you want CheckBoxes that propagate instead of TextBlocks.Microgram
I
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.

Indeed answered 6/3, 2013 at 7:13 Comment(2)
You are right, this is really the problem. Strangely enough, it works without the braces when used in ItemTemplate, but not in Resources.Arnoldarnoldo
@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.Indeed

© 2022 - 2024 — McMap. All rights reserved.