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.