HierarchicalDataTemplate and TreeView

4 ביוני 2009

6 תגובות

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.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

6 תגובות

  1. Thaddeus16 באוקטובר 2009 ב 2:20

    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?

    להגיב
  2. Donatas19 בנובמבר 2009 ב 18:39

    Did you forgot to change:

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

    to

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

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

    להגיב
  3. Kshitij22 ביוני 2011 ב 8:41

    Hi, I am using DataTables and DataRelations to do bindings for same HierarchicalDataTemplates. When I add a new row to any DataTable, it gets reflected on the TreeView. But I am unable to show that newly added row as selected in 0TreeView. Any help/guidance on how to achieve this would be appreciated

    להגיב
  4. Marat30 בנובמבר 2011 ב 8:50

    Very useful post, Thank You!

    להגיב
  5. Kim25 בדצמבר 2011 ב 1:58

    A very useful post.

    And now I understand a little more regarding TreeView. :-)

    A very good MVC pattern for setting a view specific to data type.

    Thank you.

    להגיב
  6. Oliver10 במאי 2012 ב 14:52

    And a lot of minor errors or misexplained details…
    new Composite { Name = "Division 2", Children = null }
    instead of
    new Department { Name = "Division 2", Children = null }

    להגיב