09 April, 2011

How to: Make a Hierarchical treeview binding in WPF

It has really proven a difficult task to find a simple (and understandable) example on how to perform XAML-databinding to a Hierarchical treeview as seen below. So - as a simple example and as a point of reference, here is how.
image
public interface IFolder{
    string FullPath { get; }
    string FolderLabel { get; }
    List<IFolder> Folders { get; } 
}


This is what we have to work with; a folder list as you would expect to see in the left-hand pane in Windows Explorer. The data is presented to the View using the ViewModel seen below. This ViewModel exposes a list of IFolders (List<IFolder>) as a property; a property to which the View can bind.

ViewModel
class ViewModel : INotifyPropertyChanged {
    public ViewModel()
    {
        TEST = "jubba";

        m_folders = new List<IFolder>();

        //add Root items
        Folders.Add(new Folder { FolderLabel = "Dummy1", FullPath = @"C:\dummy1" });
        Folders.Add(new Folder { FolderLabel = "Dummy2", FullPath = @"C:\dummy2" });
        Folders.Add(new Folder { FolderLabel = "Dummy3", FullPath = @"C:\dummy3" });
        Folders.Add(new Folder { FolderLabel = "Dummy4", FullPath = @"C:\dummy4" });

        //add sub items
        Folders[0].Folders.Add(new Folder { FolderLabel = "Dummy11", FullPath = @"C:\dummy11" });
        Folders[0].Folders.Add(new Folder { FolderLabel = "Dummy12", FullPath = @"C:\dummy12" });
        Folders[0].Folders.Add(new Folder { FolderLabel = "Dummy13", FullPath = @"C:\dummy13" });
        Folders[0].Folders.Add(new Folder { FolderLabel = "Dummy14", FullPath = @"C:\dummy14" });

    }

    public string TEST { get; set; }


    private List<IFolder> m_folders;
    public List<IFolder> Folders
    {
        get { return m_folders; }
        set
        {
            m_folders = value;
            NotifiyPropertyChanged("Folders");
        }
    }

    void NotifiyPropertyChanged(string property)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}



View

The interesting part in this context is the View which details the XAML-syntax. It is seen below. Note that the binding between the ViewModel and the View is declared in the XAML directly:

Capture

So, configuration of the bindings is seen below.

Simple Binding (flat structure):As seen, a simple root-level binding is achieved by using a standard <TreeView.ItemTemplate><DataTemplate>… syntax.
        <!-- simple root level binding-->
        <TextBlock Text="Simple root binding}" Foreground="Red" Margin="10,10,0,0" />
        <TreeView ItemsSource="{Binding Folders}" Margin="10">
            <TreeView.ItemTemplate>
                <DataTemplate>
                    <TreeViewItem Header="{Binding FolderLabel}"/>
                </DataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>



Non-simple Binding (hierarchical structure):The Hierarchical view is achieved using the <TreeView.ItemTemplate><HierarchicalDataTemplate>… syntax. Do note that you need to inform the HierarchicalDataTemplate of the datatype to display. This is done using the DataType attribute on the HierarchicalDataTemplate XAML-element. Do note also that the ItemsSource need to be an IEnumerable<T> implementation (List<T> in this case).
        <!-- hieracical binding -->
        <TextBlock Text="Hierarchical root binding}" Foreground="Red" Margin="10,20,0,0"/>
        <TreeView ItemsSource="{Binding Folders}" Margin="10" Height="200">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Folders}" DataType="{x:Type local:IFolder}">
                    <TreeViewItem Header="{Binding FolderLabel}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

UPDATE:
Sourcecode is here: https://onedrive.live.com/redir?resid=95576c544a336590!258599&authkey=!AGyR8mLn6nL23qY&ithint=file%2czip


Technorati Tags:

21 comments:

abatishchev said...

Thanks!

Dinu Sweet said...

Where is the Folder Class?

Claus Konrad said...

The Folder class is just a simple class with 2 string properties (label and path) as the interface {IFolder] prescribes. I did not include it in the sample, as I thought it to be not important, but maybe I should have done that? Anyway - it's just a simple class with 2 properties.

Ludovic Peyron said...

Hello, I try your code and I can generate m_folders List with parents and children Folder, but I only get the Parents Folder in my TreeList : The children Folder doesn't show.

Here is my IFolder interface :

public interface IFolder
{
List Folders { get; set; }
string FolderLabel { get; set; }
string FullPath { get; set; }
}

And here is my Folder class:

public class Folder : IFolder
{
public List Folders { get; set; }
public string FolderLabel { get; set; }
public string FullPath { get; set; }

public Folder()
{
Folders = new List();
}
}

Thank you for any help :)

Claus Konrad said...

I've added the project for your convenience above.

Jakob Gillich said...

Wow. As you said, all given examples are just to complex. Thanks to you now I got at least a result after hours of staring at a blank tree view.

KU said...

You rock. This is a simple and great example!
Thank you so much!

feelnerdy said...

Hey, I have a hard time adding items on runtime, for example if i have this code on a button press inside MainWindow:

private void Button_Click(object sender, RoutedEventArgs e)
{
((ViewModel)Tree.DataContext).Folders.Add(new Folder { FolderLabel = "Dummy5", FullPath = @"C:\dummy5" });
}

The items are not visible

Claus Konrad said...

Hi

My immediate instinct would be that the reason is that you are doing this at runtime hence the datatype is unknown (to the binding at least).
Does that make sense?

LeNeveu said...

the datatype IFolder is not even necessary...
Cheers

Anthony B said...

Thanks for the example. Very useful!

One thing I noticed when implementing the HierarchicalDataTemplate example was that I had trouble selecting the item in the tree view. I found this was resolved by not using TreeViewItem within the template as this is done automatically. I replaced it with a TextBlock and it worked well.

Thanks again.

me said...

The link to the sources of the project doesn't work. Can you check it please?

Claus Konrad said...

I'm sorry, I have changed web host and provider since. The majority of code is seen in the blog post however...

What is the problem you are seeing..?

Rodrigo Basniak said...

How do you link the ViewModel class with the treeview?

Claus Konrad said...

The class [ViewModel] is set as datacontext in the view (xaml-declared).
You have two options:
1. set it in XAML
2. set in the ctor of the view

Rodrigo Basniak said...

Ok, I did it, I had to name the treeview and then set DataContext in code, it wasnt working direct in XAML

One last question, if I remove the IFolder interface, and only use the Folder class, the code still works. It is really necessary or does this change has any hidden drawback?

Thank you so much for such a great example.

Claus Konrad said...

Using and relying on an interface is technically not needed. But, if you adhere to best practice and just common sense; it calls for a separation. You are by utilizing the Interface (IFolder) able to throw mocked unit-tests on your ViewModels to verify their functionality.

Just common sense ;-)

RWAnderson said...

Couple of comments for people trying to get this to work now:

DataTemplates do not support binding to interfaces. Not sure if this code used to work, but it doesn't work now. In the HierarchicalDataTemplate tag DataType should be {x:Type local:Folder} and not local:IFolder.

Second, HierarchicalDataTemplate tag must be within a TreeView.ItemTemplate tag in order to bind to the Folder.FolderLabel property. So it should be:







Claus Konrad said...

I can only make it work. And also against interfaces.

See code here

Anonymous said...

Nice explanation. Short and comprehensive.

Anonymous said...

It is a really cool tutorial!
It is a really nice example!
I've never thought that TreeView could be so simple!