Heterogeneous hierarchies in the WPF Elements MulticolumnTreeView
The MulticolumnTreeView (aka TreeListView) in WPF Elements combines the functionality of a TreeView and a ListView, supporting the hierarchy and expansion features of a TreeView with the multicolumn display of a ListView.
Most of the time, when you use a MulticolumnTreeView, you’ll be using a homogeneous hierarchy — that is, with the same type of object at each level. This is because the set of columns is set at the control level. If the objects at each level are too different, it doesn’t make much sense to try to present them under the same set of columns.
Sometimes, however, you may have a heterogeneous hierarchy where, although the objects in the hierarchy have different types, there’s enough commonality that a column approach makes sense.
For example, suppose we’re displaying a taxonomic hierarchy, such as the biological classification of organisms into kingdoms, phyla, species and so on. We’ll imagine that the entries in the taxonomy are represented by classes such as Kingdom, Phylum and Species. These classes have some properties in common (say, Description and LatinName), but each may have additional class-specific properties (for example, Species might have a flag indicating the species’ conservation status, or the URL of a picture of an example of the species). We’ll also assume that each class has a different name for its collection of children: the Order class has a Families collection, the Family class a Genera collection and the Genus class a Species collection.
We’ll first tackle the problem of creating the tree. With a homogeneous hierarchy, you can just tell the MulticolumnTreeView what the “children” property is, and all will be well:
<ms:MulticolumnTreeView ItemTemplate="{ms:ChildPath Children}" />But each of our classification types has a different name for its “children” collection. So this won’t work for our hierarchy.
To address this, we need to go a bit more deeply into the workings of the MulticolumnTreeView. A MulticolumnTreeView is a TreeView, so it builds its hierarchy from a HierarchicalDataTemplate (it just ignores the body of the template in favour of the column-based display). The {ms:ChildPath} syntax is a shortcut for defining a HierarchicalDataTemplate, but we don’t have to take the shortcut. As in a normal TreeView, we can explicitly create a HierarchicalDataTemplate. And, also as in a normal TreeView, as part of that HierarchicalDataTemplate we can, if we want, specify a different ItemsSource to get us to the next level of the hierarchy. So we can tell the MulticolumnTreeView how to navigate our heterogeneous hierarchy as follows:
<Window.Resources> <HierarchicalDataTemplate x:Key="SpeciesTemplate" ItemsSource="{x:Null}" /> <HierarchicalDataTemplate x:Key="GenusTemplate" ItemsSource="{Binding Species}" ItemTemplate="{StaticResource SpeciesTemplate}" /> <HierarchicalDataTemplate x:Key="FamilyTemplate" ItemsSource="{Binding Genera}" ItemTemplate="{StaticResource GenusTemplate}" /> <HierarchicalDataTemplate x:Key="OrderTemplate" ItemsSource="{Binding Families}" ItemTemplate="{StaticResource FamilyTemplate}" /> <HierarchicalDataTemplate x:Key="ClassTemplate" ItemsSource="{Binding Orders}" ItemTemplate="{StaticResource OrderTemplate}" /> </Window.Resources>
(The reason for the explicit SpeciesTemplate is that, if we didn’t have this, the TreeView would keep trying to apply the nearest template at the Species level. That would be the GenusTemplate, and that would cause binding errors when WPF tried to find the Species property, specified in GenusTemplate’s ItemsSource, on the Species class. These errors are benign, but there’s no harm in keeping things clean.)
Now that we can display all the levels of our hierarchy, what about tailoring the display of specific types? This depends on how much tailoring you need to do. For now, let’s put a little colour indicator next to the names of endangered species.
We can do this by setting the CellTemplateSelector property of the GridViewColumn (instead of the DisplayMemberBinding or CellTemplate). Our selector will examine the type of the object being templated, and return a special template if the object is a Species. Then it’s just a matter of defining the “normal” and “species” templates:
<DataTemplate x:Key="DefaultEnglishNameTemplate"> <TextBlock Text="{Binding EnglishName}" /> </DataTemplate> <DataTemplate x:Key="SpeciesEnglishNameTemplate"> <StackPanel Orientation="Horizontal"> <Rectangle Name="StatusRect" Stroke="Black" StrokeThickness="1" Fill="Orange" Height="10" Width="10" Margin="-15,0,4,0" /> <TextBlock Text="{Binding EnglishName}" /> </StackPanel> <DataTemplate.Triggers> <DataTrigger Binding="{Binding ConservationStatus}" Value="NotThreatened"> <Setter TargetName="StatusRect" Property="Visibility" Value="Hidden" /> </DataTrigger> <DataTrigger Binding="{Binding ConservationStatus}" Value="Endangered"> <Setter TargetName="StatusRect" Property="Fill" Value="Red" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> <local:ClassificationTypeSelector x:Key="ByClassificationLevel" DefaultTemplate="{StaticResource DefaultEnglishNameTemplate}" SpeciesTemplate="{StaticResource SpeciesEnglishNameTemplate}" />
Here’s the result:
What we’ve described here is limited to working within a cell (that is, a single column of the item row). If you need to create some UI that spans cells, breaks out of the cellular structure or replaces the cellular structure, then you need to start templating the MulticolumnTreeViewItems. To get started with this, check out the MulticolumnTreeViewStyling.xaml page in the Elements sample project included with the WPF Elements package. The difference in the case of heteregeneous hierarchies is that — depending on how radical your level-specific customisations are — you’ll probably need to use ItemContainerStyleSelector instead of ItemContainerStyle to determine the template.
In our example, let’s extend the conservation status colour to be the background to the row instead of just a little indicator, and add a message expanding on this. (I’ve also kept some other chrome from the Elements sample.)
First, we need a StyleSelector. This will have exactly the same logic as the DataTemplateSelector from the previous iteration, but now it’s returning Styles instead of DataTemplates.
Now we define the styles. I’m going to base these closely on the Elements sample. For my default style, in fact, I’m just going to use the Elements sample style, with some application-specific bits taken out. For my species style, I’m going to add a coloured rectangle and a TextBlock, with appropriate data bindings and triggers. Here’s the species style — I’ve left out the default style because it’s similar but simpler — and the selector declaration:
<Style x:Key="SpeciesMulticolumnTreeViewItem" TargetType="ms:MulticolumnTreeViewItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ms:MulticolumnTreeViewItem"> <Border Margin="0,2,5,2" Background="Transparent" BorderThickness="0"> <Grid> <Rectangle Fill="Orange" RadiusX="6" RadiusY="6" Name="StatusRect" Margin="{Binding Level, Converter={StaticResource LevelToIndentConverter}, RelativeSource={RelativeSource AncestorType={x:Type ms:MulticolumnTreeViewItem}}}" /> <StackPanel> <GridViewRowPresenter Name="PART_Header" Content="{TemplateBinding Header}" Columns="{Binding WrappedColumns, RelativeSource={RelativeSource AncestorType={x:Type ms:MulticolumnTreeView}}}" /> <Border Margin="{Binding Level, Converter={StaticResource LevelToIndentConverter}, RelativeSource={RelativeSource AncestorType={x:Type ms:MulticolumnTreeViewItem}}}" Padding="20,3,3,3"> <TextBlock Name="StatusText" Text="{Binding ConservationDetails}" FontFamily="Times New Roman" FontStyle="Italic" /> </Border> <ItemsPresenter Name="ItemsHost" /> </StackPanel> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsExpanded" Value="False"> <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/> </Trigger> <DataTrigger Binding="{Binding ConservationStatus}" Value="NotThreatened"> <Setter TargetName="StatusRect" Property="Visibility" Value="Hidden" /> <Setter TargetName="StatusText" Property="Visibility" Value="Hidden" /> </DataTrigger> <DataTrigger Binding="{Binding ConservationStatus}" Value="Endangered"> <Setter TargetName="StatusRect" Property="Fill" Value="Red" /> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <local:ClassificationStyleSelector x:Key="StyleByClassificationLevel" DefaultStyle="{StaticResource CustomMulticolumnTreeViewItem}" SpeciesStyle="{StaticResource SpeciesMulticolumnTreeViewItem}" />
And here’s the result:
So, to summarise:
- If everything in your hierarchy is the same (or at least the bits you’re interested in are the same), you can use a single ItemTemplate and the DisplayMemberBinding or CellTemplate properties.
- If the “children” collections have different names at different levels, use multiple HierarchicalDataTemplates and specify the ItemsSource property on each.
- To vary the appearance or content of a cell, use CellTemplateSelector.
- To vary the appearance or content of an entire row, use ItemContainerStyleSelector.
- You can mix and match these techniques as required to customise your user interface precisely to the kind of data you need to present.
4 Responses to “Heterogeneous hierarchies in the WPF Elements MulticolumnTreeView”
Leave a Reply
![]()
BrainDump (1)
Community Code (1)
Events (6)
General (31)
Lab Samples (2)
LightSpeed (132)
MegaPack (3)
News (48)
Products (64)
Projects (4)
Screencast (6)
SharePoint (1)
Silverlight (5)
Silverlight Elements (12)
SimpleDB Management Tools (11)
Visual Studio (4)
VS File Explorer (5)
WPF (31)
WPF Diagramming (14)
WPF Elements (22)
WPF Property Grid (24)
![]()
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
December 2007
November 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007



Tagged as 

Posted by Ivan Towlson on 28 April 2008



[...] in a way which is sensitive to the XML nodes and attributes. The techniques I posted about for displaying heterogeneous object hierarchies apply pretty much identically to XML, so here’s a brief primer on using XML in the WPF [...]
[...] Heterogeneous hierarchies in the WPF Elements MulticolumnTreeView [...]
[...] Heterogeneous hierarchies in the WPF Elements MulticolumnTreeView [...]
[...] wrote before about heterogeneous hierarchies in the WPF Elements MulticolumnTreeView (aka TreeListView). In that example, I assumed that, [...]