Follow Me on Instagram Subscribe via RSS Feed

9th Day of Silverlight : Implicit Data Templates

December 24, 2011

Only a couple of days left before Christmas and I’m busy wrapping up a few more posts to put them under the tree.  This one is another favorite of mine.

On the ninth day of Silverlight, the team delivered to me… implicit data templates.

Prior to Silverlight 4, all styles and data templates were explicit.  This meant that every time you referenced one you had to do so by the key name.  Silverlight 4 introduced implicit styles where you could use the new property TargetType to set the default style of a control.  This was a monumental leap in component reusability and style management.

Silverlight 5 introduced the next piece by providing implicit data templates.  The best way to really discuss implicit data templates and their benefits is to walk thru it.

Using Data Templates

Let’s get started with taking a brief look at data templates themselves.  After starting a new Silverlight project, we will create a templated ListBox.  First we need to create a data object to work with.  For this first example let’s create a simple Vehicle class:

public class Vehicle
{
    public string Make { get; set; }
    public string Model { get; set; }
    public Color Color { get; set; }
}

Now will we create a simple ListBox to display some data.

<Grid x:Name="LayoutRoot" Background="White">
    <ListBox x:Name="myList"
                Width="325"
                Height="400"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                BorderThickness="1"/>
</Grid>

The last step is to create some data in our code behind and set the ListBox’s ItemsSource property to it.

public MainPage()
{
    InitializeComponent();

    myList.ItemsSource = new List<Vehicle>()
    {
        new Vehicle() {Make = "Dodge", 
                       Model = "Charger", 
                       Color = Colors.Black},
        new Vehicle() {Make = "Ford", 
                       Model = "Mustang", 
                       Color = Colors.Red},
        new Vehicle() {Make = "Chevy", 
                       Model = "Corvette", 
                       Color = Colors.Yellow},
        new Vehicle() {Make = "Ford", 
                       Model = "Pinto", 
                       Color = Colors.Blue}
    };
}

Now if you run the example, you will get a list of items that have the full name of our Vehicle class. 

image

Not exactly a  world class list box is it?  So now let’s see a data template in action.  We will create a DataTemplate on our page and assign the ListBox’s ItemTemplate property to it.

<UserControl.Resources>
    <local:SolidColorBrushConverter x:Key="colConv"/>
    <DataTemplate x:Key="vehicleTemplate">
        <Grid Width="300">
            <Grid.Background>
                <LinearGradientBrush StartPoint="0,0"
                                        EndPoint="0,1">
                    <GradientStop Color="White" Offset="0"/>
                    <GradientStop Color="LightGray" Offset="1"/>
                </LinearGradientBrush>
            </Grid.Background>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="225"/>
                <ColumnDefinition Width="75"/>
            </Grid.ColumnDefinitions>
            <StackPanel Margin="10, 5, 0, 5">
                <TextBlock
                    FontSize="18"
                    Text="{Binding Make}"/>
                <TextBlock
                    FontSize="14"
                    Margin="0,5,0,0"
                    Text="{Binding Model}"/>
            </StackPanel>
            <Rectangle Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Height="50"
                Width="50"
                Fill="{Binding Color,
                        Converter= 
                        {StaticResource colConv}}"/>
        </Grid>
    </DataTemplate>
</UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
<ListBox x:Name="myList"
            Width="325"
            Height="400"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            ItemTemplate="{StaticResource vehicleTemplate}"
            BorderThickness="1"/>
</Grid>

You might notice I had to add a value converter in there.  The converter takes a Color and returns a SolidColorBrush.  Here is that value converter.

public class SolidColorBrushConverter : IValueConverter
{

    public object Convert(object value, 
        Type targetType, 
        object parameter, 
        System.Globalization.CultureInfo culture)
    {
        if(value is Color)
        {
            return new SolidColorBrush((Color)value);
        }

        return null;
    }

    public object ConvertBack(object value, 
        Type targetType, 
        object parameter, 
        System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Now running our application will yield a bit more information than last time. 

image

Implicate Data Templates

In the previous example we specifically set the ItemTemplate in the ListBox to our data template.  However, with implicit data templates, we no longer need to do that.  The DataTemplate class has a new property, much like the Style’s TargetType, DataType.  By setting the DataType to our Vehicle class, we our telling Silverlight to use this data template unless otherwise told.  We no longer need to set the ItemTemplate on our ListBox.  The modified XAML should look like this:

<UserControl.Resources>
    <local:SolidColorBrushConverter x:Key="colConv"/>
    <DataTemplate DataType="local:Vehicle">
        <Grid Width="300">
            <Grid.Background>
                <LinearGradientBrush StartPoint="0,0"
                                        EndPoint="0,1">
                    <GradientStop Color="White" Offset="0"/>
                    <GradientStop Color="LightGray" Offset="1"/>
                </LinearGradientBrush>
            </Grid.Background>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="225"/>
                <ColumnDefinition Width="75"/>
            </Grid.ColumnDefinitions>
            <StackPanel Margin="10, 5, 0, 5">
                <TextBlock
                    FontSize="18"
                    Text="{Binding Make}"/>
                <TextBlock
                    FontSize="14"
                    Margin="0,5,0,0"
                    Text="{Binding Model}"/>
            </StackPanel>
            <Rectangle Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Height="50"
                Width="50"
                Fill="{Binding Color,
                        Converter= 
                        {StaticResource colConv}}"/>
        </Grid>
    </DataTemplate>
</UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
<ListBox x:Name="myList"
            Width="325"
            Height="400"
            VerticalAlignment="Center"
            HorizontalAlignment="Center"
            BorderThickness="1"/>
</Grid>

Running the application you will notice that is looks identical to the last run.  Just for completion, you can change the ListBox to a ComboBox and will see that the same template is being used. 

image

Pretty slick stuff, huh?  Now it is possible to start building components that are reusable and not reliant on specific names for data templates and can be a bit more flexible.  However, as cool as that is, let’s crank it up a notch.  What if we added 3 new classes that all inherit from the Vehicle class (Car, Motorcycle, Truck)?  Let’s add these new classes, each with a unique new property :

public class Car : Vehicle
{
    public int PassengerCount { get; set; }
}

public class Motorcycle : Vehicle
{
    public string Type { get; set; }
}

public class Truck : Vehicle
{
    public int TrailerLength { get; set; }
}

Next we will update our code that is creating our collection set to use our new objects.  You might notice we kept the original Vehicle list since all of our objects inherit for the Vehicle class.

myList.ItemsSource = new List<Vehicle>()
{
    new Car() {Make = "Dodge", 
                    Model = "Charger", 
                    Color = Colors.Black,
                    PassengerCount = 5},
    new Car() {Make = "Ford", 
                    Model = "Mustang", 
                    Color = Colors.Red,
                    PassengerCount = 4},
    new Motorcycle() {Make = "Harley Davidson", 
                    Model = "Sporster", 
                    Color = Colors.Yellow,
                    Type = "Cruiser"},
    new Truck() {Make = "Peterbilt", 
                    Model = "387", 
                    Color = Colors.Blue,
                    TrailerLength = 20}
};

Now if we run our application, you will notice that nothing has changed.  That is because we are still using our Vehicle template.  However, with our new classes, maybe we would like to display different data based on the type of class.  For instance, maybe we can show the number of passengers a car can hold or the length of trailer a truck has.  In SL4 you could accomplish this by creating a custom content control and define the content based off of the class (a neat little trick I picked up from the PivotViewer team).  Now it is as simple as defining multiple data templates.  So let’s add a few more data templates to our UserControl’s resources.

<DataTemplate DataType="local:Car">
    <Grid Width="300">
        <Grid.Background>
            <LinearGradientBrush StartPoint="0,0"
                                EndPoint="0,1">
                <GradientStop Color="White" Offset="0"/>
                <GradientStop Color="LightGray" Offset="1"/>
            </LinearGradientBrush>
        </Grid.Background>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="75"/>
            <ColumnDefinition Width="75"/>
        </Grid.ColumnDefinitions>
        <StackPanel Margin="10, 5, 0, 5">
            <TextBlock
            FontSize="18"
            Text="{Binding Make}"/>
            <TextBlock
            FontSize="14"
            Margin="0,5,0,0"
            Text="{Binding Model}"/>
        </StackPanel>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Grid.Column="1">
            <TextBlock HorizontalAlignment="Center"
                        FontSize="22"
                        FontWeight="Bold"
                        Text="{Binding PassengerCount}"/>
            <TextBlock FontSize="10"
                        Text="Passengers"/>
        </StackPanel>

        <Rectangle Grid.Column="2"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Height="50"
        Width="50"
        Fill="{Binding Color,
                Converter= 
                {StaticResource colConv}}"/>
    </Grid>
</DataTemplate>
<DataTemplate DataType="local:Motorcycle">
    <Grid Width="300">
        <Grid.Background>
            <LinearGradientBrush StartPoint="0,0"
                                EndPoint="0,1">
                <GradientStop Color="White" Offset="0"/>
                <GradientStop Color="LightGray" Offset="1"/>
            </LinearGradientBrush>
        </Grid.Background>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="225"/>
            <ColumnDefinition Width="75"/>
        </Grid.ColumnDefinitions>
        <StackPanel Margin="10, 5, 0, 5">
            <TextBlock
            FontSize="18"
            Text="{Binding Make}"/>
            <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
                <TextBlock
            FontSize="14"
            Text="{Binding Model}"/>
                <TextBlock HorizontalAlignment="Center"
                    FontSize="14"
                    FontStyle="Italic"
                    Text="{Binding Type}"
                    Margin="60,0,0,0"/>
            </StackPanel>
        </StackPanel>
        <Rectangle Grid.Column="2"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Height="50"
        Width="50"
        Fill="{Binding Color,
                Converter= 
                {StaticResource colConv}}"/>
    </Grid>
</DataTemplate>

<DataTemplate DataType="local:Truck">
    <Grid Width="300">
        <Grid.Background>
            <LinearGradientBrush StartPoint="0,0"
                                EndPoint="0,1">
                <GradientStop Color="White" Offset="0"/>
                <GradientStop Color="LightGray" Offset="1"/>
            </LinearGradientBrush>
        </Grid.Background>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="75"/>
            <ColumnDefinition Width="75"/>
        </Grid.ColumnDefinitions>
        <StackPanel Margin="10, 5, 0, 5">
            <TextBlock
            FontSize="18"
            Text="{Binding Make}"/>
            <TextBlock
            FontSize="14"
            Margin="0,5,0,0"
            Text="{Binding Model}"/>
        </StackPanel>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Grid.Column="1">
            <TextBlock HorizontalAlignment="Center"
                        FontSize="22"
                        FontWeight="Bold"
                        Text="{Binding TrailerLength}"/>
            <TextBlock FontSize="10"
                        Text="Length"/>
        </StackPanel>

        <Rectangle Grid.Column="2"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Height="50"
        Width="50"
        Fill="{Binding Color,
                Converter= 
                {StaticResource colConv}}"/>
    </Grid>
</DataTemplate>

Running our application now gives us unique displays for each type of class. 

image

A thing to note is that this will still run if you keep the original Vehicle data template in there.  This is helpful when only some of the inherited classes need to have generic interfaces.  You can create an interface for the base class and use it as a fallback. 

From here, it is possible to take things a step further.  You can begin to create UI’s that are very dynamic and driven off of data templates.  For instance, you can have generic master/detail screens that the UI is defined by the binding  data type.  Pretty slick stuff if you ask me and I will dig into more of that at a later date.

The code for this example can be found here: DaysOfSilverlight_ImplicitDataTemplates.zip

Down to the final three.  As we are rolling into a big holiday weekend, I hope everyone has wonderful time celebrating with friends and family.  See you back soon…

Series Navigation<< 8th Day of Silverlight : Vector Printing10th Day of Silverlight : O/S Integration >>

Comments (4)

Trackback URL | Comments RSS Feed

  1. Ali Arslan says:

    Thanks for the great post.
    I have a question about implicit data templates;
    Is it only for ItemsControl derived controls that has an “ItemTemplate” property, or cant it be used for any control that needs a data template?
    Specifically, is possible with DataForm control that has “EditTemplate” and “ReadOnlyTemplate” data templates?

  2. Tony Champion says:

    It works for all uses of data templates.

  3. Ali Arslan says:

    Thanks for reply.

    But my trials failed with DataForm;

    I didn’t change anything in your example, but only the

    element with both

    and

    , but neither worked.

  4. Ali Arslan says:

    Sorry,
    I think xml elements doesn’t show here.

    I changed ListBox element with a DataForm element in your example, but failed.

Leave a Reply