Title: How to use Data Templates in WPF
Summary: Data templates allow us to define how a piece of
data will appear. In this article we’ll use XML binding, but everything we
do here can be applied to binding to any type of data.
To understand data templates, let’s see code example that
we had for binding. Instead of binding directly to the Title attribute, we’ll
bind to the elements:
public class Window1 : Window {
public Window1() {
XmlDocument doc = new XmlDocument();
doc.LoadXml(@”c:\SourceXML.xml”);
XmlDataProvider dataSource = new XmlDataProvider();
dataSource.Document = doc;
Binding bind = new Binding();
bind.Source = dataSource;
bind.XPath = "/Media/Book";
ListBox list = new ListBox();
list.SetBinding(ListBox.ItemsSourceProperty, bind);
Title = "XML Binding";
Content = list;
}
}
This markup produces an apparently empty list box that, in reality,
contains three empty elements, as clicking inside the list reveals. Because
no template is associated with XmlElement, the system simply calls
ToString on it, which returns an empty string because there are no child
elements under “Book”.
The DataTemplate type resembles ControlTemplate quite a bit. They
both leverage FrameworkElementFactory to define the display tree. Control-
Template defines a display tree for a control, within which we use template
binding to wire up display properties to control properties. DataTemplate,
however, defines a display tree for a data item, within which we use data
binding to wire up display properties to data properties. DataTemplate also
automatically sets the data context of the display tree to be the template
data item.
To build our first template in code, let’s create a simple text display
with the title of the book. We create a DataTemplate object associated with
XmlElement as the data type:
DataTemplate template = new DataTemplate();
template.DataType = typeof(XmlElement);
Next we have to create the visual tree for the template:
FrameworkElementFactory textFactory =
new FrameworkElementFactory(typeof(TextBlock));
We need to bind the Text property so that TextBlock will be the correct
XPath statement:
Binding bind = new Binding();
bind.XPath="@Title";
textFactory.SetBinding(TextBlock.TextProperty, bind);
Notice that there is no need to declare a data source. The DataContext
property for the instantiated visual tree is automatically set to the item to
which the template is being applied. We can now associate the factory with
the template:
template.VisualTree = textFactory;
The last step is to set the ItemTemplate property on ListBox to reference
our newly defined template. Putting all this together, we have a
simple application that displays the book titles:
Code snippet
public Window1() {
XmlDocument doc = new XmlDocument();
doc.LoadXml(...);
DataTemplate template = new DataTemplate();
template.DataType = typeof(XmlElement);
Binding bind = new Binding();
bind.XPath="@Title";
FrameworkElementFactory textFactory =
new FrameworkElementFactory(typeof(TextBlock));
textFactory.SetBinding(TextBlock.TextProperty, bind);
template.VisualTree = textFactory;
XmlDataProvider dataSource = new XmlDataProvider();
dataSource.Document = doc;
bind = new Binding();
bind.Source = doc;
bind.XPath = "/Media/Book";
ListBox list = new ListBox();
list.ItemTemplate = template;
list.SetBinding(ListBox.ItemsSourceProperty, bind);
Title = "XML Binding";
Content = list;
}
Discussion: DataTemplate and ControlTemplate have very similar functionality
and usage, and they use many of the same patterns. These resource references
are often used to bind the ItemTemplate property of a list to the template
instead. All of the code we just wrote would have been much simpler
if we had written it using XAML instead:
<Window ...
xmlns:sx='clr-namespace:System.Xml;assembly=System.Xml'
Title='XML Binding'
DataContext='{DynamicResource dataSource}'
>
<Window.Resources>
<XmlDataProvider x:Key='dataSource'>
...
</XmlDataProvider>
<DataTemplate x:Key='template' DataType='{x:Type sx:XmlElement}'>
<TextBlock Text='{Binding XPath=@Title}' />
</DataTemplate>
</Window.Resources>
<ListBox>
ItemsSource= '{Binding XPath=/Media/Book }'
ItemTemplate='{DynamicResource template}' />
</Window>
Templates are an extremely powerful mechanism for visualizing data.
In addition to building complex templates, we can also create multiple
templates and switch between then dynamically.