Published 8 August 2008
Introduction
If you’re either learning WPF (Windows Presentation Foundation) or are using WPF extensively you will without doubt have the need for DataTemplates. DataTemplates allow the developer to define how and what is bound and rendered in the User Interface from the underlying application objects. They are, to put it plainly, ubiquitous when it comes to displaying data in a WPF User Interface.
When you have a collection of objects that are to be shown in the User Interface in a logical and common way a DataTemplate is the method of choice to define the look and feel of those elements once they are rendered in the UI. They fuel consistency and are intrinsically reusable. The demo source code included with this article is very simple but if you combine these ideas with other possibilities, such as value converters, you will soon find that you have a very powerful set of UI tools at your disposal.
There are many, many sources of information on Code Project and the Internet in general that can explain the general DataTemplate principals better than I. The best place to start in order to learn some of the fundamentals of defining and using DataTemplates will be the MSDN documentation that can be found here:
MSDN – DataTemplates
Inline Templates
DataTemplates are often defined ‘inline’ meaning that they are constructed within an items control XAML structure in the main ‘consuming’ XAML file such as:
<ListBox Width="400" Margin="10" ItemsSource="{Binding Source={StaticResource myTodoList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Or they are often contained within the Resources section of a Window, Page or Control such as:
<Window.Resources>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
Alternatives
One other method is possible which doesn’t actually seem to get much press. To be honest I personally feel the method I’m covering here is most ‘agreeable’ in relation to the whole Devigner paradigm that WPF has thrown at us. You can use UserControls as DataTemplates! This is such a neat solution that I sat down to put together this little demo of how this is done so that others can get an easy look at the idea and start using this method.
One point that is continually made about WPF is its ability to allow ‘loose coupling’ of application logic and User Interface implementations. By making UserControls that are designed to be used as DataTemplates you can really take this further and have clean separation between the main Window or Page XAML and the DataTemplates. Anyway, lets look at some code!!
Using The Code
For this demo I started off by creating a normal Visual Studio 2008 solution. Then I defined a DataItem class to hold some data to be shown in the UI:
public sealed class DataItem
{
public string Name { get; set; }
public int Age { get; set; }
public double Progress { get; set; }
public DataItem(string name, int age, double progress)
{
Name = name;
Age = age;
Progress = progress;
}
}
Now that we have a simple object defined that we want to display in the UI we need to setup the consumption of this object and populate it with some data. To do this I simply defined an ObservableCollection in the window and created a method that stuffed in some DataItem objects into the collection:
ObservableCollection<DataItem> items = new ObservableCollection<DataItem>();
Within the main window code-behind file I added this method:
void PopulateDataItems()
{
items.Add(new DataItem("Jammer", 32, 10.0));
items.Add(new DataItem("John", 44, 20.0));
items.Add(new DataItem("Jane", 25, 30.0));
items.Add(new DataItem("Robert", 30, 40.0));
items.Add(new DataItem("Jezzer", 50, 50.0));
items.Add(new DataItem("James", 40, 60.0));
items.Add(new DataItem("Rebecca", 25, 70.0));
items.Add(new DataItem("Mark", 35, 80.0));
items.Add(new DataItem("Leah", 20, 90.0));
items.Add(new DataItem("WallE", 700, 100.0));
}
Done! We now have a window code-behind that defines a collection and adds the correct objects to that collection ready for display in the UI. The next thing to look at is the XAML that uses all of this WPF goodness.
First of all I created a UserControl (ProgressReporter.xaml) to display the Progress property of the DataItem class:
<UserControl x:Class="UserControlAsDataTemplateDemo.UserControls.ProgressReporter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ProgressControl"
Height="Auto" Width="Auto">
<Grid>
<ProgressBar x:Name="UIReporter"
Value="{Binding PercentToShow, ElementName=ProgressControl}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Style="{DynamicResource ProgressReporterStyle}" />
</Grid>
</UserControl>
The code-behind for this control also defines a custom DependencyProperty that the ProgressBar uses to obtain its value from:
public static DependencyProperty PercentProperty =
DependencyProperty.Register("PercentToShow",
typeof(double), typeof(ProgressReporter));
public double PercentToShow
{
get { return (double)GetValue(PercentProperty); }
set { SetValue(PercentProperty, value); }
}
Next up is the UserControl that is going to be used as the DataTemplate.
<UserControl x:Class="UserControlAsDataTemplateDemo.UserControls.ItemTemplateControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UserControls="clr-namespace:UserControlAsDataTemplateDemo.UserControls"
Height="Auto" Width="Auto">
<Border BorderThickness="2,2,2,2"
CornerRadius="5,5,5,5"
Background="#FF626262"
BorderBrush="#FFFFAC00"
Grid.ColumnSpan="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Text="{Binding Path=Name}"
TextWrapping="Wrap"
Margin="4,4,4,4"
Grid.ColumnSpan="1"/>
<TextBox HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Text="{Binding Path=Age}"
TextWrapping="Wrap"
Grid.Column="1"
Margin="4,4,4,4"
Grid.ColumnSpan="1"/>
<UserControls:ProgressReporter
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
PercentToShow="{Binding Path=Progress}"
Grid.Column="2" Margin="4,4,4,4" />
</Grid>
</Border>
</UserControl>
The main points to note here are the Binding settings. You can see that the text boxes are bound to the Name and Age properties of the DataItem class and that the custom DependencyProperty called PercentToShow is used to bind the double property Progress from the DataItem class to the ProgressReporter control.
The image below shows the DataTemplate UserControl in Expression Blend:
Now that we have the ‘DataTemplate’ UserControl defind the only thing left to do is write the XAML in the MainWindow that will use this control as a DataTemplate. The XAML for this looks like:
<ListBox x:Name="peopleListBox"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemContainerStyle="{StaticResource ListBoxItemStretch}"
Foreground="Transparent"
BorderBrush="Transparent"
Background="Transparent"
Grid.ColumnSpan="2">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<UserControls:ItemTemplateControl Margin="4" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So when you run the application and hit the [Load Data] button it uses the PopulateDataItems() method and then binds the ObservableCollection ‘items’ to the listbox and renders each DataItem using our UserControl:
private void btnLoadData_Click(object sender, RoutedEventArgs e)
{
// Reset the Items Collection
items.Clear();
// Populate the Items Collection
PopulateDataItems();
// Bind the items Collection to the List Box
peopleListBox.ItemsSource = items;
}
Adding in Dynamic Behaviour using Code-Behind
Once you have defined and used your custom UserControl as a DataTemplate you will more than likely wish to make them a bit smarter and more WPF’y. There are a variety of ways to do this through the use of mechanisms like Triggers, DataTriggers and Value Converters. One thing that we can now do in addition to this is simply use the C# code-behind file to build in some dynamic behaviour into the DataTemplate. You will find the complete source for this dynamic example in the included source file at the top of this article suffixed ‘Dynamic’.
In order to fully show this dynamic behaviour I have made a few changes to the data creation methods used in the original sample, I have randomised the progress value being used in the DataItems by doing this:
private void PopulateDataItems()
{
items.Add(new DataItem("Jammer", 32, MakeRandomDouble()));
items.Add(new DataItem("John", 44, MakeRandomDouble()));
items.Add(new DataItem("Jane", 25, MakeRandomDouble()));
items.Add(new DataItem("Robert", 30, MakeRandomDouble()));
items.Add(new DataItem("Jezzer", 50, MakeRandomDouble()));
items.Add(new DataItem("James", 40, MakeRandomDouble()));
items.Add(new DataItem("Rebecca", 25, MakeRandomDouble()));
items.Add(new DataItem("Mark", 35, MakeRandomDouble()));
items.Add(new DataItem("Leah", 20, MakeRandomDouble()));
items.Add(new DataItem("WallE", 700, MakeRandomDouble()));
}
private double MakeRandomDouble()
{
int randomnumber = _random.Next(1, 100);
return (double)randomnumber;
}
To drive the dynamic behaviour that will react to this random data I have added in code into the Loaded event of the ItemTemplateControl:
Loaded="UserControl_Loaded"
The Loaded event is fired after the data binding has taken place and after Layout has been run on the element. This means we have access to the actual data values in order to react accordingly to these values and make the changes we want too just before display in the UI. The code in this event handler is as follows:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (Convert.ToInt32(this.AgeTextBox.Text) > 40)
{
SolidColorBrush brush = new SolidColorBrush();
brush.Color = Colors.DarkGray;
MainBorder.Background = brush;
}
if (this.ProgressReporter.PercentToShow > 80.0)
{
SolidColorBrush brush = new SolidColorBrush();
brush.Color = Colors.Lavender;
MainBorder.Background = brush;
}
// An extension method to create and start an animation
Animations.Fade(this, 0.0, (ProgressReporter.PercentToShow / 100), 1000);
}
This is all fairly straightforward stuff. One major point to make here is that we have given some controls names using the x:Name=””, this allows us to easily access these controls in the associated class. We are reacting to the numeric value of the AgeTextBox.Text and the PercentToShow value of the ProgressReporter. In each case we are then creating a new SolidColorBrush object and applying that to the background color of the MainBorder object in order to highlight the control in the UI based on the bound values. You will also note that we are using an extension method that wraps a DoubleAnimation called Fade(); …
In order to add in some more WPFness I have created a new static class called Animations that looks like this:
public static class Animations
{
static Animations()
{
}
public static void Fade(this UIElement control,
double sourceOpacity,
double targetOpactity,
int milliseconds)
{
control.BeginAnimation(UIElement.OpacityProperty,
new DoubleAnimation(sourceOpacity,targetOpactity,
new Duration(TimeSpan.FromMilliseconds(milliseconds))));
}
}
And there we go we have now added in some pretty cool dynamic behaviour using just the code-behind of a UserControl. This could be massively expanded on but would over complicate the example.
Its a Wrap(per)
One of the main benefits of this approach from my point of view is that when designing DataTemplates it’s easy to feel disconnected from the actual design process. By using a UserControl as a DataTemplate you are afforded the full design flow of using Expression Blend to write the XAML for the DataTemplate. Personally I find that to be a compelling reason to use this approach for most DataTemplate designs unless you are using a very basic design combined with predefined styles for any elements within that DataTemplate you may need.
Either way I hope this little demo has been useful and that you find some uses for this approach. This is merely skimming the potential!
23 July 2008 – Initial Version
View article on CodeProject.com
Published 8 August 2008
UserControlAsDataTemplateDemo
UserControlAsDataTemplateDemoDynamic
Introduction
If you’re either learning WPF (Windows Presentation Foundation) or are using WPF extensively you will without doubt have the need for DataTemplates. DataTemplates allow the developer to define how and what is bound and rendered in the User Interface from the underlying application objects. They are, to put it plainly, ubiquitous when it comes to displaying data in a WPF User Interface.
When you have a collection of objects that are to be shown in the User Interface in a logical and common way a DataTemplate is the method of choice to define the look and feel of those elements once they are rendered in the UI. They fuel consistency and are intrinsically reusable. The demo source code included with this article is very simple but if you combine these ideas with other possibilities, such as value converters, you will soon find that you have a very powerful set of UI tools at your disposal.
There are many, many sources of information on Code Project and the Internet in general that can explain the general DataTemplate principals better than I. The best place to start in order to learn some of the fundamentals of defining and using DataTemplates will be the MSDN documentation that can be found here:
MSDN – DataTemplates
Inline Templates
DataTemplates are often defined ‘inline’ meaning that they are constructed within an items control XAML structure in the main ‘consuming’ XAML file such as:
Or they are often contained within the Resources section of a Window, Page or Control such as:
Alternatives
One other method is possible which doesn’t actually seem to get much press. To be honest I personally feel the method I’m covering here is most ‘agreeable’ in relation to the whole Devigner paradigm that WPF has thrown at us. You can use UserControls as DataTemplates! This is such a neat solution that I sat down to put together this little demo of how this is done so that others can get an easy look at the idea and start using this method.
One point that is continually made about WPF is its ability to allow ‘loose coupling’ of application logic and User Interface implementations. By making UserControls that are designed to be used as DataTemplates you can really take this further and have clean separation between the main Window or Page XAML and the DataTemplates. Anyway, lets look at some code!!
Using The Code
For this demo I started off by creating a normal Visual Studio 2008 solution. Then I defined a DataItem class to hold some data to be shown in the UI:
Now that we have a simple object defined that we want to display in the UI we need to setup the consumption of this object and populate it with some data. To do this I simply defined an ObservableCollection in the window and created a method that stuffed in some DataItem objects into the collection:
Within the main window code-behind file I added this method:
Done! We now have a window code-behind that defines a collection and adds the correct objects to that collection ready for display in the UI. The next thing to look at is the XAML that uses all of this WPF goodness.
First of all I created a UserControl (ProgressReporter.xaml) to display the Progress property of the DataItem class:
The code-behind for this control also defines a custom DependencyProperty that the ProgressBar uses to obtain its value from:
Next up is the UserControl that is going to be used as the DataTemplate.
The main points to note here are the Binding settings. You can see that the text boxes are bound to the Name and Age properties of the DataItem class and that the custom DependencyProperty called PercentToShow is used to bind the double property Progress from the DataItem class to the ProgressReporter control.
The image below shows the DataTemplate UserControl in Expression Blend:
Now that we have the ‘DataTemplate’ UserControl defind the only thing left to do is write the XAML in the MainWindow that will use this control as a DataTemplate. The XAML for this looks like:
So when you run the application and hit the [Load Data] button it uses the PopulateDataItems() method and then binds the ObservableCollection ‘items’ to the listbox and renders each DataItem using our UserControl:
Adding in Dynamic Behaviour using Code-Behind
Once you have defined and used your custom UserControl as a DataTemplate you will more than likely wish to make them a bit smarter and more WPF’y. There are a variety of ways to do this through the use of mechanisms like Triggers, DataTriggers and Value Converters. One thing that we can now do in addition to this is simply use the C# code-behind file to build in some dynamic behaviour into the DataTemplate. You will find the complete source for this dynamic example in the included source file at the top of this article suffixed ‘Dynamic’.
In order to fully show this dynamic behaviour I have made a few changes to the data creation methods used in the original sample, I have randomised the progress value being used in the DataItems by doing this:
To drive the dynamic behaviour that will react to this random data I have added in code into the Loaded event of the ItemTemplateControl:
The Loaded event is fired after the data binding has taken place and after Layout has been run on the element. This means we have access to the actual data values in order to react accordingly to these values and make the changes we want too just before display in the UI. The code in this event handler is as follows:
This is all fairly straightforward stuff. One major point to make here is that we have given some controls names using the x:Name=””, this allows us to easily access these controls in the associated class. We are reacting to the numeric value of the AgeTextBox.Text and the PercentToShow value of the ProgressReporter. In each case we are then creating a new SolidColorBrush object and applying that to the background color of the MainBorder object in order to highlight the control in the UI based on the bound values. You will also note that we are using an extension method that wraps a DoubleAnimation called Fade(); …
In order to add in some more WPFness I have created a new static class called Animations that looks like this:
And there we go we have now added in some pretty cool dynamic behaviour using just the code-behind of a UserControl. This could be massively expanded on but would over complicate the example.
Its a Wrap(per)
One of the main benefits of this approach from my point of view is that when designing DataTemplates it’s easy to feel disconnected from the actual design process. By using a UserControl as a DataTemplate you are afforded the full design flow of using Expression Blend to write the XAML for the DataTemplate. Personally I find that to be a compelling reason to use this approach for most DataTemplate designs unless you are using a very basic design combined with predefined styles for any elements within that DataTemplate you may need.
Either way I hope this little demo has been useful and that you find some uses for this approach. This is merely skimming the potential!
23 July 2008 – Initial Version
UserControlAsDataTemplateDemo
UserControlAsDataTemplateDemoDynamic
View article on CodeProject.com