David Sackstein's Blog

"The more that you learn, the more places you'll go.”, Dr. Seuss

HierarchicalDataTemplate and TreeView

This post is about flexible styling for databound TreeViews in WPF. We will see how nested trees of any depth and structure can be bound easily with the HierarchicalDataTemplate.

In the next posts I will demonstrate use of the Composite Pattern to describe the hierarchical data and LINQ to XML to read the data from an XML file. Finally, I will put all the pieces together in an application that displays a trivial organization chart.

We will start with a set of classes that all have Composite as their base class. Composite might be defined like so:

    class Composite

    {

        public string Name { get; set; }

        public List<Composite> Children { get; set; }

    }

Here is method that builds some sample data:

    private List<Composite> GetData()

    {

        List<Composite> list = new List<Composite>()

        {

            new Composite { Name = "1", Children = new List<Composite>()

                {

                    new Composite { Name = "1.1", Children = new List<Composite>()

                        {

                            new Composite { Name = "1.1.1" },

                            new Composite { Name = "1.1.2" },

                            new Composite { Name = "1.1.3" }

                        }

                   

                    new Composite { Name = "2.1", Children = new List<Composite>()

                        {

                            new Composite { Name = "2.1.1" },

                            new Composite { Name = "2.1.2" },

                            new Composite { Name = "2.1.3" }

                        }

                    }

                }

            },

            new Composite { Name = "3", Children = null }

        };

        return list;

    }

We would like to bind this data to a TreeView named treeView like so:

    treeView.ItemsSource = GetData();

Here is a first attempt at the XAML for treeView.

    <TreeView Name="treeView">

         <TreeView.ItemTemplate>

            <DataTemplate>

               <TextBlock Text="{Binding Path=Name}" />

            </DataTemplate>

         </TreeView.ItemTemplate>

     </TreeView>

But this results in the display of the top level nodes only – like a ListBox.

The key is to introduce the HierarchicalDataTemplate like so:

   <TreeView Name="treeView">

      <TreeView.ItemTemplate>

         <HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">

            <TextBlock Text="{Binding Path=Name}" />

         </HierarchicalDataTemplate>

      </TreeView.ItemTemplate>

   </TreeView>

This is the result:

image

So, the HierarchicalDataTemplate is used to style a current item and to generate items for those in its ItemsSource.

What happens if the second level nodes have a different type with different properties and we would like to present each type differently?

For instance, let’s define:

    private static List<Composite> GetData()

    {

        List<Composite> list = new List<Composite>()

        {

            new Department { Name = "Division 1", Children = new List<Composite>()

                {

                    new Department { Name = "Finance", Budget = 10, Children = new List<Composite>()

                        {

                            new Employee { Name = "Tom", Role="Accountant" },

                            new Employee { Name = "Sarah", Role="Accountant" },

                            new Employee { Name = "Harry", Role="Secretary" }

                        }

                    },

                    new Department { Name = "Sales", Children = new List<Composite>()

                        {

                            new Employee { Name = "Alice", Role="", Salary=11 },

                            new Employee { Name = "Mary", Role="", Salary=11 },

                            new Employee { Name = "Harry", Role="", Salary=11 }

                        }

                    }

                }

            },

            new Composite { Name = "Division 2", Children = null }

The result, below is disappointing, but not surprising. All nodes look the same, because we are still using the same template for items of all types.

image

As you know, different DataTemplates can be applied automatically according to type by setting the DataType property.

We use this in the improved version of the XAML below. Here, there is a HierarchicalTemplate for each type.

   <Window.Resources>

 

      <HierarchicalDataTemplate DataType="{x:Type org:Department}"

                               ItemsSource="{Binding Path=Children}">

         <Border BorderBrush="Green"

                BorderThickness="1">

            <StackPanel Orientation="Horizontal">

               <TextBlock Text="Department: " />

               <TextBlock Text="{Binding Path=Name}" />

            </StackPanel>

         </Border>

      </HierarchicalDataTemplate>

 

      <HierarchicalDataTemplate DataType="{x:Type org:Manager}"

                               ItemsSource="{Binding Path=Children}">

         <Border BorderBrush="Blue"

                BorderThickness="1">

            <StackPanel Orientation="Vertical">

               <StackPanel Orientation="Horizontal">

                  <TextBlock Text="Manager: "></TextBlock>

                  <TextBlock Text="{Binding Path=Name}" />

                  <StackPanel Orientation="Horizontal">

                     <TextBlock Text=" Salary (" />

                     <TextBlock Text="{Binding Path=Salary}" />

                     <TextBlock Text=")" />

                  </StackPanel>

               </StackPanel>

               <StackPanel Orientation="Horizontal">

                  <TextBlock Text="{Binding Path=EmployeeCount}" />

                  <TextBlock Text=" employees" />

               </StackPanel>

            </StackPanel>

         </Border>

      </HierarchicalDataTemplate>

 

      <HierarchicalDataTemplate DataType="{x:Type org:Employee}"

                               ItemsSource="{Binding Path=Children}">

         <Border BorderBrush="Red"

                BorderThickness="1">

            <StackPanel Orientation="Horizontal">

               <TextBlock Text="Employee: " />

               <TextBlock Text="{Binding Path=Name}" />

               <TextBlock Text=" Salary (" />

               <TextBlock Text="{Binding Path=Salary}" />

               <TextBlock Text=")" />

            </StackPanel>

         </Border>

      </HierarchicalDataTemplate>

 

    </Window.Resources>

All templates are in the Windows.Resources collection and the TreeView markup now looks like this:

    <TreeView Name="treeView"/>

And this is the result:

image

Summary

In this post I demonstrated how to use the HierarchicalDataTemplate to bind data to a TreeView.

Using HierarchicalDataTemplate there is no need to hard code the structure of the tree in the markup. The structure of the tree is determined by the structure data that we bind to it.

My next post is about the representation of the business objects and the databinding.

Comments

David Sackstein's Blog said:

In the previous posts I have shown how I used the Composite Pattern to describe hierarchical data , to

# June 6, 2009 2:19 AM

David Sackstein's Blog said:

Technorati Tags: WPF , DataBinding , TreeView In this post we will see how to use WPF’s TreeView with

# September 2, 2009 1:19 PM

Thaddeus said:

Does this approach work in Silverlight 3?  DataType does not appear to be a property of HierarchicalDataTemplate anymore?  Am I missing something?  Or is there a new method of achieving this?

# October 16, 2009 2:20 AM

Donatas said:

Did you forgot to change:

new Department { Name = "Finance", Budget = 10, Children = new List<Composite>()

to

new Department { Name = "Finance", Budget = 10, Children = new List<Employee>() ???

if not then how did you add a Department class to a list of Composite??

# November 19, 2009 6:39 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: