Non-recursive WPF TreeView controls

A WPF TreeView control can display data in any tree structure. There are essentially two kinds of tree structures: recursive and non-recursive. Non-recursive trees are simpler.

SurveyDataModelItems in a recursive tree can have children of the same type. Items in a non-recursive tree cannot. A non-recursive tree has a fixed depth, and every item on the same depth is the same type as its siblings. This makes it particularly easy to model in WPF.

Pictured here is a non-recursive tree structure for a survey game. A Game has Surveys, which in turn have Answers. It's a very simple tree structure with no recursion. The depth of this tree from the Game to the Answers will always be 3.

Binding the TreeView
Let's start with a TreeView control that displays the surveys. We'll bind the collection to the ItemsSource of the TreeView.

<TreeView ItemsSource="{Binding Surveys}">
</TreeView>

The TreeView control creates a TreeViewItem for each element in the collection. We need to tell the container how to populate those TreeViewItems. We do this with a DataTemplate.

A DataTemplate defines the contents of an item in a container control. DataTemplates are used for things like list boxes, menus, and tree controls. A DataTemplate does not define the item itself -- the container determines that. ListBox controls have ListBoxItems. TreeView controls have TreeViewItems. The DataTemplate just defines what goes into that item.

We want each TreeViewItem to contain a TextBlock bound to the survey question. So we set the ItemTemplate property to an instance of the DataTemplate class. This DataTemplate contains a TextBox with the right binding.

<TreeView ItemsSource="{Binding Surveys}">
    <TreeView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Question}"/>
        </DataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

We want each TreeViewItem to have child items. The child items are not contained within the parent item. They are just a property of the parent item. We can't use a DataTemplate to set properties. So we switch to a special DataTemplate: HierarchicalDataTemplate.

HierarchicalDataTemplate
A HierarchicalDataTemplate is a DataTemplate with an additional property: ItemsSource. This property is bound to a child collection. The template sets this property on the TreeViewItem itself.

<TreeView ItemsSource="{Binding Surveys}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Answers}">
            <TextBlock Text="{Binding Question}"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

This template will set the ItemsSource of each Survey TreeViewItem to the bound collection of Answers. But, as before, we need to tell the control what to put into those TreeViewItems. We do this by setting the ItemTemplate property of the HierarchicalDataTemplate.

<TreeView ItemsSource="{Binding Surveys}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Answers}">
            <TextBlock Text="{Binding Question}"/>
            <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Response}"/>
                </DataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Because the Answer TreeViewItem does not need to have children, there is no need to use a HierarchicalDataTemplate at this level.

A non-recursive tree structure is easy to model. Since an item does not have children of its own type, there are no self-referential templates. When the tree structure can be recursive, however, things are a bit tricker. That's coming up.

Leave a Reply

You must be logged in to post a comment.