Introduction
In this article, I will describe how to display data contained in a simple XML file using a TreeView
control in a WPF application without writing any code.
In this article, all of the magic is found in the XAML file that defines the window contents. Admittedly, you would not try to write a business application
like this, but there may be something to learn here that will make that business application go together a little more easily.
Background
This article draws on information gleaned from two other articles about using XML with WPF that you may wish to read. The first is called
Reaction to: A Simple WPF Explorer Tree by Josh Smith.
In that article, Smith demonstrates how to use a DataTemplate
to define the contents of TreeViewItem
s, and how the tree structure
can be built using the window's "Loaded" event handler. The second article by Karl Shifflett took off from Smith's article. Shifflett's article is called
Over Reaction To: A Simple WPF Explorer Tree. Shifflett's article described
the HierarchicalDataTemplate
demonstrating how it could be used to help recurse tree structure data to populate a TreeView
control.
I found these two articles trying to find a solution to populate a TreeView
with directory and file names similar to Window Explorer's directory tree,
but including at least some filenames as well. While working that out, I had issues with strange behavior in the TreeView
and decided to run some tests with XML
data. That proved to be a bit of a challenge itself, but with information gleaned from Shifflett and Smith's articles, and a little use of the grey
matter, I came up with the solution described below to populate a TreeView
control with XML data.
Using the Code
It is actually fairly easy to display XML data using WPF. Other than the XML file itself, almost all of the work to enable this is done within XAML, and most
of that is done by defining several resources. The source code available with this article is a complete working sample. The project is configured to use .NET
4.0, but you can move MainWindow.xaml and testdata.xml into a WPF 3.0 project, and it should work just as well.
In the text below, I will highlight just a sample of the XAML code that makes this work.
When you create a WPF project, a skeleton window is created in a file called MainWindow.xaml which opens in the Visual Studio editor. A little XAML
is included to configure the default window with a Grid
control to hold the rest of the visual elements of the window. In our code, we add a new child to the Window
element right before the opening Grid
tag, and below the opening Window
tag, to create a section within Window
to define some resources. To start out,
the resources section looks like this:
<!---->
<Window.Resources>
</Window.Resources>
Then between those Window.Resources
tags, we add several elements that will be used by the UI to display the XML data. The first resource
we add is an XmlDataProvider
. XmlDataProvider
is a class that makes it possible to bind XML data to elements and work with the XML
structure. An object is created by creating a resource from it like this:
<XmlDataProvider x:Key="xmldata"
Source="testdata.xml"
XPath="/root" />
The Source
attribute is set to the name of the XML file we created for the test, but it does not actually reference a file during run time. By putting the
file in the root directory of the project, and setting its build action to "Resource", the contents of the file are actually made a part of the assembly
during compile time. So in this case, the filename becomes the key name for the resource, and WPF is smart enough to work out the details when the application
runs. The XPath
attribute is used to specify a node. The XML test file has a root node called "root", so we use that here to start the XML parsing with that node.
The really interesting work is done in data templates created with HierarchicalDataTemplate
. Again, within the Window.Resources
element, right below the XmlDataProvider
, we create a data template for each type of tag used within the XML test file. In this case, three node items
exist within testdata.xml, "root", "Node", and "leaf". So we create three data templates. The template for "Node" looks like this:
<HierarchicalDataTemplate DataType="Node"
ItemsSource="{Binding XPath=./*}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0"
Text="Node:" />
<TextBlock Margin="5,0,0,0"
Text="{Binding XPath=@name}" />
</StackPanel>
</HierarchicalDataTemplate>
The other templates are similar. The main difference to note is that the ItemsSource
attribute was not specified for "leaf" nodes, because I
knew that I placed no items within "leaf" nodes. Such nodes are always at the end of the line, so there is no reason to check them for contents.
The ItemsSource
attribute is provided by HierarchicalDataTemplate
specifically for this use. HierarchicalDataTemplate
uses
ItemsSource
to determine if the current item contains other items. If not, the node will be rendered accordingly, and navigation further down into
the node is impossible. But if other items are contained within the current item, it will be rendered so that the user can drill down into the node to see the other items.
The last thing to do is to bind the XML data to the UI controls. I do that by setting the DataContext
of the Grid
, and the ItemsSource
attribute of the TreeView
control. The complete UI is defined within just a few lines.
<Grid DataContext="{StaticResource xmldata}">
<TreeView Name="dirTree"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding}"
VirtualizingStackPanel.IsVirtualizing="False"
VirtualizingStackPanel.VirtualizationMode="Standard" />
</Grid>
That's it!
Points of Interest
The HierarchicalDataTemplate
class made it very easy to populate the TreeView
with XML data. There is no reason it could not be used
in other scenarios to help in more complicated situations. The main things to remember are to create a template for each type expected by the TreeView
(or else a parent type to catch several descending types) and to set the ItemsSource
on types that may contain more data that needs to go into the TreeView
.
History
- January 20, 2012 - Original article.
- January 21, 2012 - Cosmetic changes to correct code alignment.