WPF Simplified Part 8: Data Templates

In WPF, a control can specify how it wants to display a .NET object, by specifying a UI template for the object. Thus, when the object is bound to the control, the object is displayed using the (data) template for it that the control has specified. This data template overrides the control’s visual tree just like a control template. The DataTemplate can determine both the visual aspect of how the data is presented and also how data binding accesses different properties of the data object. You can store the data template for an object as a resource and have different controls use the same data template to display the object, so that the object’s UI representation is similar across different controls. Also, by applying different data templates to the same data, you can flexibly change the visual appearance of the data in your application.

Consider the simple XAML,

<Window x:Class="WpfApplication3.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Button Height="100" Width="100" />
    </Grid>
</Window>

And say we want to display this simple .NET object,

public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
}

We can do this by defining a DataTemplate and assigning to the button’s ContentTemplate,

<Button Height="100" Width="100" Content="{Binding}">
    <Button.ContentTemplate>
        <!-- Define a DataTemplate for the Person type... --> 
        <DataTemplate DataType="{x:Type local:Person}">
            <!-- ...with a custom visual tree for the data object... -->
            <TextBlock>
                <!-- ...and binding to different properties of the data object -->
                <TextBlock Text="{Binding Name}" />
                (age:<TextBlock Text="{Binding Age}" />)
            </TextBlock>
        </DataTemplate>
    </Button.ContentTemplate>
</Button>
 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;
 

The XAML above creates a DataTemplate asociated with the Person type. This data template has its own visual tree (consisting of TextBlocks or anything else we could want). Now when the button’s Content is bound to an object of type Person, the data template will handle the way the button displays the Person object.

Say we want to reuse this DataTemplate (that is associated with the Person type), we can declare it as a resource and any control can then use the data template to display the Person object.

<Grid>
    <Grid.Resources>       
        <!-- Define a DataTemplate for the Person type -->
        <DataTemplate DataType="{x:Type local:Person}">
            <TextBlock>
                <TextBlock Text="{Binding Name}" />
                (age:<TextBlock Text="{Binding Age}" />)
            </TextBlock>
        </DataTemplate>
    </Grid.Resources>
    
    <!-- The button's Content is bound to a Person type -->
    <Button Height="100" Width="100" Content="{Binding}" />
    
    <!-- The radiobutton's Content is bound to a Person type -–> 
    <RadioButton>
        <RadioButton.Content>
            <Binding />
        </RadioButton.Content>
    </RadioButton>

</Grid>
 

// And in code, do this, 
Person person = new Person() { Name = "John", Age = 32 }; 
this.DataContext = person; 
 

DataTemplates can be applied to both content controls and item controls. For content controls, the ContentTemplate property is set to a DataTemplate, and for item controls the ItemTemplate property is set to a DataTemplate. The following is a partial list of the controls that support DataTemplates,

   Content controls using the  
   ContentTemplate property
     Item controls using the

     ItemTemplate property
     Other properties that can

     be set to a DataTemplate
                        Button                   ComboBox               CellTemplate
                     CheckBox                 ContextMenu           HeaderTemplate
                         Label                      ListBox      ColumnHeaderTemplate
                 RadioButton                     ListView     SelectedContentTemplate
                  TabControl                    StatusBar    SelectionBoxItemTemplate
                     ToolTip                     ToolBar
                     Window                    TreeView  

 

There are three ways we can change the data template based on properties of the bound data object:

1. Data triggers:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication3"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.Resources>
           
            <!-- Define a named DataTemplate -->
            <DataTemplate DataType="{x:Type local:Person}">
                <!-- Custom visual tree of how the data will be presented -->
                <TextBlock>
                    <!-- Binding to different properties of the data object -->
                    <TextBlock Text="{Binding Name}" />
                    (age:<TextBlock x:Name="AgeTextBlock" Text="{Binding Age}" />)
                </TextBlock>
    
                <!-- Add a trigger to the template... –>
                <DataTemplate.Triggers>
                    <!-- ...that binds to the Age property of the data -->
                    <DataTrigger Binding="{Binding Path=Age}">
                        <!-- ...and if the Age is 32 –>
                        <DataTrigger.Value>
                            <System:Int32>32</System:Int32>
                        </DataTrigger.Value>
                        <!-- ...we’ll change the font color to red -->
                        <Setter TargetName="AgeTextBlock" Property="Foreground" Value="Red" />
                    </DataTrigger>
                </DataTemplate.Triggers>

            </DataTemplate>
        </Grid.Resources>
 
        <!-- The button's Content is bound to a Person type via code -->
        <Button Height="100" Width="100" Content="{Binding}" />
    </Grid>
</Window>
 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;

We could also use a trigger inside a Style to change the DataTemplate of the button,

<Grid>
    <Grid.Resources>
 
        <!-- Define a Style element... -->
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <!-- ...with a watch on the binding object’s Name property -->
                <DataTrigger Binding="{Binding Path=Name}" Value="John">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <!-- define any custom UI here -->
                                <TextBlock Text="{Binding Path=Name}" Foreground="Red" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>

    </Grid.Resources>
    <!-- The button's Content is bound to a Person type via code -->
    <Button Height="100" Width="100" Content="{Binding}" />
</Grid>

 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;

or we could set the ContentTemplate to a named DataTemplate,

<Grid>
    <Grid.Resources>
 
        <!-- Define a named data template -->        
        <DataTemplate x:Key="FirstTemplate">
            <TextBlock Text="{Binding Path=Name}" Foreground="Red" />
        </DataTemplate>

        <!-- Define a Style element -->
        <Style TargetType="{x:Type Button}">
            <Style.Triggers>
                <!-- ...with a watch on the binding object’s Name property -->
                <DataTrigger Binding="{Binding Path=Name}" Value="John">
                    <!-- apply the named data template -->
                    <Setter Property="ContentTemplate" Value="{StaticResource FirstTemplate}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>

    </Grid.Resources>
    <!-- The button's Content is bound to a Person type via code -->
    <Button Height="100" Width="100" Content="{Binding}" />
</Grid>

 
// And in code, do this,
Person person = new Person() { Name = "John", Age = 32 };
this.DataContext = person;

2. Template Selector: Use the DataTemplateSelector class to choose the template you want to apply based on the bound object’s properties. In XAML,

<Grid>
    <Grid.Resources>
        <!-- Define a named DataTemplate -->
        <DataTemplate x:Key="firstTemplate">
            <TextBlock>
                <TextBlock Text="{Binding Name}" />
                (age:<TextBlock Text="{Binding Age}" />)
            </TextBlock>
        </DataTemplate>

        <!-- Define a named DataTemplate -->
        <DataTemplate x:Key="secondTemplate">
            <TextBlock>
                <TextBlock Text="{Binding Age}" />
                (name:<TextBlock Text="{Binding Name}" />)
            </TextBlock>
        </DataTemplate>
 
        <!-- Select the template based on the code behind -->
        <local:PersonTemplateSelector x:Key="templateSelector"
            FirstTemplate="{StaticResource firstTemplate}"
            SecondTemplate="{StaticResource secondTemplate}" />

    </Grid.Resources>
    
    <!-- Add the TemplateSelector property -->
    <Button Height="100" Width="100" Content="{Binding}" 
                         ContentTemplateSelector="{StaticResource templateSelector}" />
</Grid>
 

And in the code behind,

/// <summary>
/// The template selector class
/// </summary>
public class PersonTemplateSelector : DataTemplateSelector {
    // Define a set of properties that represent all the templates 
    // that this selector can return.
    public DataTemplate FirstTemplate { get; set; }
    public DataTemplate SecondTemplate { get; set; }

    public override DataTemplate SelectTemplate (object item, DependencyObject container) {

        if (item != null) {
            Person person = (Person)item;

            // Return a template based on the object’s properties,
            if (person.Age < 32)
                return FirstTemplate;
            else
                return SecondTemplate;
        }
        return null;
    }
}
 

3. Value Converter: Takes an incoming bound object property value and returns a changed UI property value. This is the topic of a future post.

Digg This
Advertisements

About soumya chattopadhyay
I live and work in Seattle, WA. I work with Microsoft technologies, and I'm especially interested in C#.

5 Responses to WPF Simplified Part 8: Data Templates

  1. Pingback: WPF Simplified Series « I.Net

  2. Narendra Agrawal says:

    what is exact diff between Data template and Control Template? Give some real time scenario when to use Datatemplate and when to use ControlTemplate? Can’t we use DataTemplate in place of control template in style and vice a versa.

  3. Pingback: Fix Wpf Error Template Binding Windows XP, Vista, 7, 8 [Solved]

  4. Pingback: Fix Wpf Error Template Tooltip Windows XP, Vista, 7, 8 [Solved]

  5. The firewall although blocks the entire website so they won’t be in a position to access any
    of the games.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: