Change Templates Dynamically in WPF
WPF has the flexibility to modify the look of your controls dynamically at runtime with just a few lines of code. Take a look at Figures 1 and 2 and you will see two different views of the same list box and data. To accomplish this, you simply need to setup two different resources of XAML code that you can switch between at runtime.
Figure 1: More detail
Figure 2: Less detail
The Basic Form and the Data
This XAML form uses an XML file to fill in the ListBox control with the product data. There is a folder called \Images where the pictures for the various products are located. Below is a fragment of the XML file that contains the product data.
<Products>
<Product>
<ProductId>1</ProductId>
<ProductName>PDSA .NET Productivity Framework</ProductName>
<Price>5000</Price>
<Logo>/Images/Framework.gif</Logo>
</Product>
...
...
</Products>
An XmlDataProvider control is used to load this file and supply the data for the list box. The XAML code shown below is the setup of this XmlDataProvider. This XmlDataProvider is located within the Window.Resources section of this WPF Window.
<XmlDataProvider x:Key="xProducts"
Source="/Product.xml"
XPath="Products/Product" />
As you can see in the above code the Product.xml file is located in the root of the project. You use the XPath attribute to read to the node level where the actual data is within the XML file. Then you will be able to specify the element name of where the data is for displaying in the list box.
Create the XAML Templates
The next items in the Window.Resources section of this WPF Window are the <DataTemplates>. There are two data templates created as resources in this window. One has a key of “tmplMore” and one has a key of “tmplLess”. Below is the XAML code that is used to create the list box that looks like Figure 1. You can see the use of a Horizontal StackPanel control, and an Image control as the first control within that StackPanel.
<DataTemplate x:Key="tmplMore">
<StackPanel Orientation="Horizontal">
<Image Margin="8"
Source="{Binding XPath=Logo}"
Width="100"></Image>
<Label Width="250"
Content="{Binding XPath=ProductName}"></Label>
<Label Width="100"
Content="{Binding XPath=Price}"></Label>
</StackPanel>
</DataTemplate>
Notice the data binding of the Source property of the image and the Content properties of the two labels. The Binding uses XPath attribute to signify the name of the element within the XML file. Remember that the XmlDataProvider has the first part of the XPath query, while each Binding is the element where the data is located that will be displayed in the list box.
The next data template created within the Window.Resources section has the key of “tmplLess” and looks like the XAML below. You can see that the Image control is missing from this template.
<DataTemplate x:Key="tmplLess">
<StackPanel Orientation="Horizontal">
<Label Width="250"
Content="{Binding XPath=ProductName}"></Label>
<Label Width="100"
Content="{Binding XPath=Price}"></Label>
</StackPanel>
</DataTemplate>
In the WPF Window is the definition of the list box control shown in the XAML listing below. Notice that the ItemsSource attribute is set to bind to the xProducts XmlDataProvider control. Also notice that the ItemTemplate is bound to the “tmplMore” data template resource. This is the default data template that is used when the window is first displayed.
<ListBox Margin="10,10,0,0"
Height="300"
Name="lstData"
ItemsSource="{Binding Source={StaticResource xProducts}}"
IsSynchronizedWithCurrentItem="True"
ItemTemplate="{StaticResource tmplMore}">
</ListBox>
Switch Between the XAML Templates
You will write code in each button’s click event procedures, shown below, to switch between the two XAML templates. In each event procedure you create a variable to refer to a DataTemplate object. You then use the FindResource method of the current window to find the resource named either “tmplMore” or “tmplLess”. Once you have the data template object you can assign that to the ItemTemplate property of the list box control.
One thing to note is that you should include error handling within this code because if the FindResource method does not find the resource, then it will throw an exception. I purposely left out the code for brevity in this example.
Visual Basic
Private Sub btnMore_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs) Handles btnMore.Click
Dim tmpl As DataTemplate
tmpl = DirectCast(Me.FindResource("tmplMore"), DataTemplate)
lstData.ItemTemplate = tmpl
End Sub
Private Sub btnLess_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs) Handles btnLess.Click
Dim tmpl As DataTemplate
tmpl = DirectCast(Me.FindResource("tmplLess"), DataTemplate)
lstData.ItemTemplate = tmpl
End Sub
C#
private void btnMore_Click(object sender, RoutedEventArgs e)
{
DataTemplate tmpl;
tmpl = (DataTemplate)this.FindResource("tmplMore");
lstData.ItemTemplate = tmpl;
}
private void btnLess_Click(object sender, RoutedEventArgs e)
{
DataTemplate tmpl;
tmpl = (DataTemplate)this.FindResource("tmplLess");
lstData.ItemTemplate = tmpl;
}
Summary
In this article you learned to change templates on a list box control dynamically at runtime using the FindResource method. You simply need to create a couple of XAML templates that you will use to switch between the different views. You create these XAML templates in the Window.Resources section of the window. You give each template a unique name, and then write a little code to find each of the resources at runtime. Once you have located the appropriate resource, you assign it to the ItemTemplate property of the list box and WPF will change the look of the control instantly.
NOTE: You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "WPF Change Templates" from the drop-down.
Good Luck With Your Coding,
Paul Sheriff
** SPECIAL OFFER FOR MY BLOG READERS **
Visit http://www.pdsa.com/Event/Blog for a free eBook on "Fundamentals of N-Tier".
Past Blog Content
Blog Archive
-
2015
-
2014 (18)
-
2013 (11)
-
2012 (19)
-
2011 (29)
-
2010 (19)
-
2009 (28)
-
2008 (0)
-
2007 (14)
-
2006 (6)