Unleash the Power of Implicit Data Templates in Silverlight 5

One of the cool new features in Silverlight 5 are implicit data templates.  They allow developers to associate a set of visuals encapsulated inside a data template with a specific class.  You can find a cool demo of this feature on Tim Heuer;s blog.  One of the links in this article shows how to use the feature inside a list box.

I would like to demonstrate ultimate power of this feature when it comes to creating views dynamically based on a specific type.  Let me elaborate.  I want to create an entry screen for a person class using MVVM pattern.  First, I am creating a simple Person class:

 

 

namespace SL5Features.Models

{

  public class Person : ModelBase

  {

 

    private string firstName;

 

    public string FirstName

    {

      get { return firstName; }

      set { firstName = value; OnPropertyChanged("FirstName"); }

    }

 

    private string lastName;

 

    public string LastName

    {

      get { return lastName; }

      set { lastName = value; OnPropertyChanged("LastName"); }

    }

 

  }

}

 

My base class does nothing more than implements INotifyPropertyChanged interface:

using System.ComponentModel;

 

namespace SL5Features.Models

{

  public class ModelBase : INotifyPropertyChanged

  {

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)

    {

      if (PropertyChanged != null)

      {

        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

      }

    }

  }

}

 

Now, I want to create a default template for this class.  Unlike other demos you saw so far, I am going to add this template to a resource dictionary.  I am going to create a new folder in my project called Templates.  Then, I am going to right-click, select Add New Item, then select resource dictionary type.  Once dictionary is created, I am going to add a data template to it, and associate it with the person type.  I have to add namespace to my models of course.  Here is what my dictionary looks like:

<ResourceDictionary

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:models="clr-namespace:SL5Features.Models">

 

  <DataTemplate DataType="models:Person">

    <Grid>

      <Grid.RowDefinitions>

        <RowDefinition Height="Auto"/>

        <RowDefinition Height="7"/>

        <RowDefinition Height="Auto"/>

      </Grid.RowDefinitions>

      <Grid.ColumnDefinitions>

        <ColumnDefinition Width="Auto"/>

        <ColumnDefinition Width="7"/>

        <ColumnDefinition Width="*"/>

      </Grid.ColumnDefinitions>

     

      <TextBlock Text="First Name:" 

                Grid.Column="0" Grid.Row="0" 

                HorizontalAlignment="Right"

                VerticalAlignment="Center"/>

     

      <TextBlock Text="Last Name:" 

                Grid.Column="0" Grid.Row="2" 

                HorizontalAlignment="Right"

                VerticalAlignment="Center"/>

     

      <TextBox Text="{Binding Path=FirstName, Mode=TwoWay}"

              Grid.Column="2" Grid.Row="0"/>

     

      <TextBox Text="{Binding Path=LastName, Mode=TwoWay}" 

              Grid.Column="2" Grid.Row="2"/>

    </Grid>

  </DataTemplate>

</ResourceDictionary>

 

Pretty simple screen.  Now for giggles I am going to create new dictionary with slightly different implementation for the same UI:

<ResourceDictionary

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:models="clr-namespace:SL5Features.Models">

 

  <DataTemplate DataType="models:Person">

    <Grid>

      <Grid.RowDefinitions>

        <RowDefinition Height="Auto"/>

        <RowDefinition Height="7"/>

        <RowDefinition Height="Auto"/>

      </Grid.RowDefinitions>

      <Grid.ColumnDefinitions>

        <ColumnDefinition Width="Auto"/>

        <ColumnDefinition Width="7"/>

        <ColumnDefinition Width="*"/>

      </Grid.ColumnDefinitions>

     

      <TextBlock Text="First Name:" 

                Grid.Column="0" Grid.Row="0" 

                HorizontalAlignment="Right"

                VerticalAlignment="Center"

                Foreground="Red"/>

     

      <TextBlock Text="Last Name:" 

                Grid.Column="0" Grid.Row="2" 

                HorizontalAlignment="Right"

                VerticalAlignment="Center"

                Foreground="Red"/>

     

      <TextBox Text="{Binding Path=FirstName, Mode=TwoWay}"

              Grid.Column="2" Grid.Row="0"

              Background="Yellow"/>

     

      <TextBox Text="{Binding Path=LastName, Mode=TwoWay}" 

              Grid.Column="2" Grid.Row="2"

              Background="Yellow"/>

    </Grid>

  </DataTemplate>

</ResourceDictionary>

 

Now, the fun part of injecting the visuals into my view.  I am going to inject them into MainPage, but ordinarily you would create a new user control.  All I do is add ContentControl and set Content to my view model’s property called Model that will contain my Person instance:

<UserControl 

   x:Class="SL5Features.MainPage"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   xmlns:models="clr-namespace:SL5Features.Models"

   mc:Ignorable="d"

   d:DesignHeight="300" d:DesignWidth="400">

 

  <Grid x:Name="LayoutRoot">

    <ContentControl 

     x:Name="Host" 

     HorizontalAlignment="Stretch" 

     VerticalAlignment="Stretch" 

     HorizontalContentAlignment="Stretch" 

     VerticalContentAlignment="Stretch"

     Content="{Binding Path=Model}"

     />

  </Grid>

</UserControl>

 

Oh, I did not show you my view models structure.  I have a base class with Model property and INotifyPropertyChanged implementation.  Simple and basic:

using System.ComponentModel;

 

namespace SL5Features.ViewModels

{

  public class ViewModelBase<T> : INotifyPropertyChanged

  {

 

    private T model;

    public T Model

    {

      get { return model; }

      set { model = value; OnPropertyChanged("Model"); }

    }

 

 

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)

    {

      if (PropertyChanged != null)

      {

        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

      }

    }

  }

}

 

In my person view model I am going to cheat and just create an instance of a person:

using SL5Features.Models;

 

namespace SL5Features.ViewModels

{

  public class PersonViewModel : ViewModelBase<Person>

  {

    public PersonViewModel()

    {

      Model = new Person() { FirstName = "Sergey", LastName = "Barskiy" };

    }

  }

}

 

Now, in App.Xaml.cs I am going to inject the view model into my main page:

    private void Application_Startup(object sender, StartupEventArgs e)

    {

      AddDictionaries();

      this.RootVisual = new MainPage() { DataContext = new PersonViewModel() };

    }

 

Now, if you run the application you will not get the desired result.  Last step is to show you what magic my AddDictionaries contains:

 

    private void AddDictionaries()

    {

      // do we have a template query parameter

      var hasTempalteKey = HtmlPage.Document.QueryString.ContainsKey("Template");

      // create Url for default visuals dictionary

      var uri = new Uri("/SL5Features;component/Templates/Templates.xaml", UriKind.Relative);

 

      // now if I have query parameter called Template set to "ALT", I am going to use differnt Url

      if (hasTempalteKey && HtmlPage.Document.QueryString["Template"].ToUpper().Contains("ALT"))

      {

        // alternate tempalte Url

        uri = new Uri("/SL5Features;component/Templates/AltTemplates.xaml", UriKind.Relative);

      }

      // now creat new dictionary and inject it into Application resources

      ResourceDictionary dictionary = new ResourceDictionary();

      dictionary.MergedDictionaries.Add(

        new ResourceDictionary() { Source = uri });

      this.Resources.MergedDictionaries.Add(dictionary);

    }

 

I commented the code to demonstrate what I am doing.  What my goal is to swap visuals based on query string containing Template=Alt.  Now, run the application .  Here is what it looks like:

 

image

Now, the cool part.  Just alter the Url and whatch the magic:

image

As you can see, I have swapped the visuals by dynamically swapping templates.  You can imagine that you can design template and use then as a replacement for your views, injecting them into pre-defined shells.  You can see how you can decrease the amount of code needed for view / view model construction in a typical MVVM application, but also dynamically inject different visuals without changing any code.

You can download my sample application here.

Enjoy Silverlight 5!  You can find the download links here.

Leave a Reply

Your email address will not be published. Required fields are marked *