Home » Blog

rounded header

WPF Elements MulticolumnTreeView and XML data

tag icon Tagged as General, WPF, WPF Elements

A commenter on a previous post asks about using XML data in a TreeListView and specifically about how to vary the display 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 Elements MulticolumnTreeView.

I’m going to use the same taxonomic hierarchy as the previous sample, but now we need to look at the XML format. Here’s a fragment:

<Class EnglishName="Mammals" LatinName="Mammalia">
  <Orders>
    <Order EnglishName="Carnivorous mammals" LatinName="Carnivora">
      <Families>
        <Family EnglishName="Cats" LatinName="Felidae">
          <Genera>
            <Genus EnglishName="Cat" LatinName="Felis">
              <Species>
                <Species EnglishName="Wild cat" LatinName="Felis silvestris" />
                <Species EnglishName="Domestic cat" LatinName="Felis silvestris catus" />
                <Species EnglishName="Jungle cat" LatinName="Felis chaus" />
              </Species>
            </Genus>
            <Genus EnglishName="Big cats" LatinName="Panthera">
              <Species>
                <Species EnglishName="Lion" LatinName="Panthera leo" 
                         ConservationStatus="Vulnerable"
                         ConservationDetails="Estimated 30-50% decline over the last two decades" />
                <Species EnglishName="Tiger" LatinName="Panthera tigris" 
                         ConservationStatus="Endangered"
                         ConservationDetails="Wild population may be as low as 2500 mature breeding individuals" />
                <Species EnglishName="Leopard" LatinName="Panthera pardus" />
              </Species>
            </Genus>
          </Genera>
        </Family>
    </Order>
  </Orders>
</Class>

The structure of this file is very regular; the only wrinkle is that I have all these collection nodes (Orders, Families, Genera) which just serve as groupers within the XML and shouldn’t be displayed. So my hierarchy declaration looks like this:

<HierarchicalDataTemplate x:Key="TaxonomyXml" ItemsSource="{Binding XPath=child::*/child::*}" />

Note the use of XPath in the ItemsSource binding. Our data templates also need to be updated to use XPath:

<DataTemplate x:Key="DefaultEnglishNameTemplate">
  <TextBlock Text="{Binding XPath=@EnglishName}" />
</DataTemplate>
 
<DataTemplate x:Key="LatinNameTemplate">
  <TextBlock Text="{Binding XPath=@LatinName}" />
</DataTemplate>

What about the customisation? In the object example, we could look at the type of object to determine whether to use our custom Species template (with the conservation status flash) or the plain text template. In the XML case, we instead look at the XmlElement that the HierarchicalDataTemplate has given us. We have access to all its properties and attributes, but in this case the element name is sufficient to tell us whether to use the Species template:

public class ClassificationTypeSelectorXml : DataTemplateSelector
{
  public DataTemplate SpeciesTemplate { get; set; }
  public DataTemplate DefaultTemplate { get; set; }
 
  public override DataTemplate SelectTemplate(object item, DependencyObject container)
  {
    XmlElement element = (XmlElement)item;
    if (element.LocalName == "Species")
    {
      return SpeciesTemplate;
    }
    else
    {
      return DefaultTemplate;
    }
  }
}

I won’t spell out the custom Species template or the selector declaration: they’re the same as in the previous article, but with property bindings converted to XPath bindings just as in the plain text templates, and of course using our new selector type. We do have one thing to watch out for: our XML doesn’t include a ConservationStatus attribute when a species has the default value of NotThreatened. So we need to add another trigger with a Value of “{x:Null}” to ensure that the endangerment flash is correctly hidden when the attribute is not present. (We could probably have avoided this if I’d made the default template behaviour line up with the default values in the first place, but you live, you learn.)

With that done, and an XmlDataProvider set up to load in the file, here’s the MulticolumnTreeView declaration to tie it all together:

<ms:MulticolumnTreeView Margin="10" Grid.Row="0"
                        ItemsSource="{Binding Source={StaticResource xml}, XPath=Class/Orders/Order}" ItemTemplate="{StaticResource TaxonomyXml}">
  <ms:MulticolumnTreeView.Columns>
    <GridViewColumn Header="English Name" CellTemplateSelector="{StaticResource ByClassificationLevel}" Width="250" />
    <GridViewColumn Header="Latin Name" CellTemplate="{StaticResource LatinNameTemplate}" Width="200" />
  </ms:MulticolumnTreeView.Columns>
</ms:MulticolumnTreeView>

Again, if you have more extreme display customisation needs, you can continue to use the techniques from the previous article, with similar changes to what we’ve already seen: use element names instead of types and XPath instead of properties, and everything should work in exactly the same way.

Leave a Reply

Data Products Visual Controls Community Store
LightSpeed ORM
NHibernate Designer
SimpleDB Tools
SharePoint Tools
WPF Elements
WPF Diagrams
Silverlight Elements
Forums
Blog
Register
Login
Subscribe to newsletter
Buy Now
My Account
Volume Discounts
Purchase Orders
Contact Us