Tuesday, January 11, 2011

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: