Change Templates Dynamically in Silverlight

Silverlight 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.

More Detail 
Figure 1: More detail

Less Detail 
Figure 2: Less detail

The Product Class

For this example, I will be using a simple Product class with four properties, and a list of Product objects using the Generic List class. Create a Product class as shown in the following code:

public class Product {
  #region Constructors

  public Product(){ }

  public Product(int id, string name, string type, decimal price)
  {
    this.ProductId = id;
    this.ProductName = name;
    this.ProductType = type;
    this.Price = price;
  }
  #endregion

  public int ProductId { get; set; }
  public string ProductName { get; set; }
  public string ProductType { get; set; }
  public decimal Price { get; set; }
}

Next, create a Product collection class that will create a mock set of product objects and fill in a property called DataCollection with this mock set of products.

public class Products : List<Product>
{
  public Products()
  {
    BuildCollection();
  }

  public ObservableCollection<Product> DataCollection { get; set; }
   
  public ObservableCollection<Product> BuildCollection()
  {
    DataCollection = new ObservableCollection<Product>();

    DataCollection.Add(new Product(1,
       "Haystack Code Generator for .NET", "Product", 799));
    DataCollection.Add(new Product(4,
       "Fundamentals of N-Tier eBook", "Book", 20));

    ... // More data here

    DataCollection.Add(new Product(10,
       "PDSA .NET Productivity Framework", "Product", 2500));

    return DataCollection;
  }
}

The Silverlight Page

To create this Silverlight page, you will need to layout a couple of different areas. First you need a top part where the two buttons are located, then you need a ListBox below that. A Grid is ideal for laying out this type of scenario. Below is the XAML for the <Grid> portion of this page.

<Grid x:Name="LayoutRoot"
      Background="White">
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <StackPanel Grid.Row="0"
              Margin="10"
              Orientation="Horizontal">
    <Button Name="btnMore"
            Margin="5"
            Tag="tmplMore"
            Content="More"
            Click="ChangeTemplate" />
    <Button Name="btnLess"
            Margin="5"
            Tag="tmplLess"
            Content="Less"
            Click="ChangeTemplate" />
  </StackPanel>
  <ListBox Margin="10"
            Grid.Row="1"
            Name="lstData"
            ItemTemplate="{StaticResource tmplMore}"
            ItemsSource="{Binding
                        Source={StaticResource productCollection},
                        Path=DataCollection}" />
</Grid>

Create an Instance of Products in your XAML

You need to have your XAML user control create an instance of your Products class. You can do that in XAML by first adding a namespace to your <UserControl> element that references the assembly in which your Products class is located.

xmlns:data="clr-namespace:Silverlight_Data"

Next, you add a <UserControl.Resources> section and create the instance of the products class from the Silverlight_Data namespace referenced by the local XAML namespace that you called "data".

<UserControl.Resources>
  <data:Products x:Key="productCollection" />
</UserControl.Resources>

The XAML above creates an instance of the Products class and assigns it the key name of "productCollection". If you look at the <ListBox> element declared earlier you see that this key name is used in the ItemsSource binding.

Create the XAML Templates

The next items that add to the <UserControl.Resources> section of the XAML are the <DataTemplates>. There are two data templates created as resources in this user control. One has a key of "tmplMore" and one has a key of "tmplLess". Below is the XAML used to create the list box that looks like Figure 1.

<DataTemplate x:Key="tmplMore">
  <StackPanel Orientation="Vertical"
              HorizontalAlignment="Left"
              Margin="8">
    <TextBlock FontSize="16"
                Text="{Binding Path=ProductName}" />
    <StackPanel Orientation="Horizontal">
      <TextBlock FontSize="12"
                  Margin="0,0,4,0"
                  Text="Type: " />
      <TextBlock FontSize="12"
                  Text="{Binding Path=ProductType}" />
      <TextBlock FontSize="12"
                  Margin="20,0,4,0"
                  Text="Price: " />
      <TextBlock FontSize="12"
                  Text="{Binding Path=Price, StringFormat=c}" />
    </StackPanel>
  </StackPanel>
</DataTemplate>

The next data template in the UserControl.Resources section has the key of "tmplLess". This template is used to create the page shown in Figure 2. The XAML to create this page looks like the following:

<DataTemplate x:Key="tmplLess">
  <StackPanel Orientation="Horizontal">
    <TextBlock FontSize="16"
                Text="{Binding Path=ProductName}" />
  </StackPanel>
</DataTemplate>

Notice that the "tmplMore" data template is assigned to the ListBox as a default in the XAML listed above. However, this is just so you have something to look at in design mode. You can modify this template dynamically at runtime. Let’s now look at how to accomplish that.

Switch XAML Templates at Runtime

You write code to respond to each button’s click event. The same event procedure is used for each button. Notice the Tag property is set to the key name of the data template you created in the UserControl.Resources section.

<Button Name="btnMore"
        Margin="5"
        Tag="tmplMore"
        Content="More"
        Click="ChangeTemplate" />
<Button Name="btnLess"
        Margin="5"
        Tag="tmplLess"
        Content="Less"
        Click="ChangeTemplate" />

Now, let’s look at the ChangeTemplate() event procedure that is called by each of these button’s Click events.

C#

private void ChangeTemplate(object sender, RoutedEventArgs e)
{
  DataTemplate dt;

  dt = (DataTemplate)this.Resources[
      ((Button)sender).Tag.ToString()];

  lstData.ItemTemplate = dt;
}


VB.NET
Private Sub ChangeTemplate(ByVal sender As System.Object, _
 ByVal e As System.Windows.RoutedEventArgs) _
  Handles btnMore.Click, btnLess.Click
  Dim dt As DataTemplate

  dt = CType(Me.Resources( _
        CType(sender, Button).Tag.ToString()),  _
          DataTemplate)

  lstData.ItemTemplate = dt
End Sub

Using the Resources property of the UserControl class you can locate the resource with the name you pass to the indexed property. The name you pass is the Tag property of the button that fired this event. 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 exception handling code for brevity in this example.

Summary

That is all there is to it. If you run this sample you will dynamically switch between the two templates at runtime. You simply create as many XAML templates as you want that will allow your user to switch between different views of the same list box. 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 Silverlight will change the look of the control instantly.

NOTE: You can download the complete sample code (in both VB and C#) at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then " Change Templates Dynamically in Silverlight" 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".

No Comments