|
|
|
Data Templates
Introduction
Data Template are a similar concept as Control Templates. They give you a very flexible and powerful solution to replace the visual appearance of a data item in a control like ListBox, ComboBox or ListView. In my opinion this is one of the key success factory of WPF.
If you don't specify a data template, WPF takes the default template that is just a TextBlock. If you bind complex objects to the control, it just calls ToString() on it. Within a DataTemplate, the DataContext is set the data object. So you can easily bind against the data context to display various members of your data object
DataTemplates in Action: Building a simple PropertyGrid
Whereas it was really hard to display complex data in a ListBox with WinForms, its super easy with WPF. The following example shows a ListBox with a list of DependencyPropertyInfo instances bound to it. Without a DataTemplate you just see the result of calling ToString() on the object. With the data template we see the name of the property and a TextBox that even allows us to edit the value.
<!-- Without DataTemplate -->
<ListBox ItemsSource="{Binding}" />
<!-- With DataTemplate -->
<ListBox ItemsSource="{Binding}" BorderBrush="Transparent"
Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Key" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBox Grid.Column="1" Text="{Binding Value }" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
How to use a DataTemplateSelector to switch the Template depending on the data
Our property grid looks nice so far, but it would be much more usable if we could switch the editor depending on the type of the property.
The simplest way to do this is to use a DataTemplateSelector . The DataTemplateSelector has a single method to override: SelectTemplate(object item, DependencyObject container) . In this method we decide on the provided item which DataTemplate to choose.
The following exmple shows an DataTemplateSelector that decides between tree data templates:
public class PropertyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultnDataTemplate { get; set; }
public DataTemplate BooleanDataTemplate { get; set; }
public DataTemplate EnumDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
DependencyPropertyInfo dpi = item as DependencyPropertyInfo;
if (dpi.PropertyType == typeof(bool))
{
return BooleanDataTemplate;
}
if (dpi.PropertyType.IsEnum)
{
return EnumDataTemplate;
}
return DefaultnDataTemplate;
}
}
<Window x:Class="DataTemplates.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:DataTemplates"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Window.Resources>
<!-- Default DataTemplate -->
<DataTemplate x:Key="DefaultDataTemplate">
...
</DataTemplate>
<!-- DataTemplate for Booleans -->
<DataTemplate x:Key="BooleanDataTemplate">
...
</DataTemplate>
<!-- DataTemplate for Enums -->
<DataTemplate x:Key="EnumDataTemplate">
...
</DataTemplate>
<!-- DataTemplate Selector -->
<l:PropertyDataTemplateSelector x:Key="templateSelector"
DefaultnDataTemplate="{StaticResource DefaultDataTemplate}"
BooleanDataTemplate="{StaticResource BooleanDataTemplate}"
EnumDataTemplate="{StaticResource EnumDataTemplate}"/>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding}" Grid.IsSharedSizeScope="True"
HorizontalContentAlignment="Stretch"
ItemTemplateSelector="{StaticResource templateSelector}"/>
</Grid>
</Window>
How to react to IsSelected in the DataTemplate
If you want to change the appearance of a ListBoxItem when it is selected, you have to bind the IsSelected property of the ListBoxItem. But this is a bit tricky, you have to use a relative source with FindAcestor to navigate up the visual tree until you reach the ListBoxItem.
<DataTemplate x:Key="DefaultDataTemplate">
<Border x:Name="border" Height="50">
...
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource=
{RelativeSource Mode=FindAncestor, AncestorType=
{x:Type ListBoxItem}},Path=IsSelected}" Value="True">
<Setter TargetName="border" Property="Height" Value="100"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Last modified: 2009-10-08 16:50:58
Copyright (c) by Christian Moser, 2011.
Comments on this article
Show all comments
|
Stephen | |
|
Commented on 15.December 2009 |
I think I just decided to learn WPF...
|
|
|
|
sathya | |
|
Commented on 11.February 2010 |
Hi Christian,
Thank you very much, i got very good basics of WPF, it is really helpfull, i really appreciate your work.Thank you, could you give me guideline how to study WCF bascis, you can send to my mail id sathyajan1@gmail.com. thank you again, i appreciate your work.
|
|
|
|
Yadav | |
|
Commented on 11.March 2010 |
Very helpful article and I would further appreciate if you give the UI prinout before and after the application of the template. Thank you.
|
|
|
|
ward | |
|
Commented on 20.April 2010 |
how can i use TreeView control to show SQL-database data in hierarchical view using c sharp.. please help me here because am working on windows from application project . iit's my graduation project.
|
|
|
|
Confused | |
|
Commented on 21.June 2010 |
This is a poor example. Where is the code behind? Where is the binding? What is this magic data you are adding to the list view?
|
|
|
|
Confused | |
|
Commented on 22.June 2010 |
This is a poor example. Where is the code behind? Where is the binding? What is this magic data you are adding to the list view?
|
|
|
|
Confused | |
|
Commented on 22.June 2010 |
This is a poor example. Where is the code behind? Where is the binding? What is this magic data you are adding to the list view?
|
|
|
|
tim | |
|
Commented on 28.June 2010 |
WOW
|
|
|
|
danielwu | |
|
Commented on 23.July 2010 |
Mr Confused,
Before you make conclusion, you'd better finish the whole article and then you will find sample solution for download in the end...
|
|
|
|
Shankarnag | |
|
Commented on 24.August 2010 |
Its really very good information about data templates.
|
|
|
|
Nikola | |
|
Commented on 1.October 2010 |
Great article, just what I was looking for. Great web site.
|
|
|
|
Bala | |
|
Commented on 9.November 2010 |
Nice
|
|
|
|
Hemanath | |
|
Commented on 9.November 2010 |
Worst discussion...lost interest in WPF
|
|
|
|
Bala | |
|
Commented on 9.November 2010 |
No Hemanath...It is not like that...It is really worth reading...WPF rocks...
|
|
|
|
Salem5 | |
|
Commented on 27.November 2010 |
Thank you, this really saved me a lot of time plowing though Hubers WPF Handbook. It is a worthy read, but I need a few results now.
|
|
|
|
KMC | |
|
Commented on 15.December 2010 |
I downloaded the sample and all it has it a bunch of checkboxes and textbox, i don't see any datatemplaing in the working...did you upload the wrote source codes?
|
|
|
|
Jamie | |
|
Commented on 10.February 2011 |
Wow, I worked for a very long time on how to put together a dynamic property editor, and you make it look so easy. The key was Grid.IsSharedSizeScope and ColumnDefinition.SharedSizeGroup. Well done and thanks!
|
|
|
|
jhon | |
|
Commented on 3.March 2011 |
good chaka...
|
|
|
|
unruledboy | |
|
Commented on 30.March 2011 |
truely awesome, I have been looking for this functionality for a long time, I will put it into test and come back to you
|
|
|
|
unruledboy | |
|
Commented on 30.March 2011 |
Hi, I tried you method, all the text content are not rendered(the TextBlock showing nothing, even I place a button and bind Name for the content, still nothing). I am sure there is data, and before using data template, it's showing.
<DataTemplate x:Key="DefaultDataTemplate">
<StackPanel Orientation="Horizontal">
<tree:RowExpander />
<CheckBox IsChecked="{Binding Selected}" />
<Image Height="16" Margin="2, 0, 5, 0" Source="{Binding Converter={StaticResource TaskImageConverter}}" Width="16" />
<TextBlock Style="{StaticResource TaskItemStyle}" Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<my:ResultCellTemplateSelector x:Key="templateSelector"
DefaultDataTemplate="{StaticResource DefaultDataTemplate}"/>
<Grid>
<tree:TreeList x:Name="tlResults">
<tree:TreeList.View>
<GridView>
<GridView.Columns>
<GridViewColumn Width="500" Header="Name" CellTemplateSelector="{StaticResource templateSelector}">
</GridViewColumn>
<GridViewColumn Header="Size" Width="90" DisplayMemberBinding="{Binding Converter={StaticResource FileSizeConverter}}" />
</GridView.Columns>
</GridView>
</tree:TreeList.View>
</tree:TreeList>
</Grid>
|
|
|
|
unruledboy | |
|
Commented on 30.March 2011 |
sorry, it's my mistake, I used the template from another control. It's working fine now, thank you very much!
|
|
|
|
Wes | |
|
Commented on 15.April 2011 |
Hi,
This is the best PropertyGrid Tutorial I found. The best part is,
it also have code to select DataTemplate depend on DataType of each class property: bool use BooleanDataTemplate which is a checkbox; Enum use EnumDataTemplate which is a combobox.
I also like go a little further, add custom type: lookup, which will show as dropdown list, but the value list come from database. e.g.
property name: car manufacturere, value come from "SELECT LOOKUP_VALUE FROM LOOKUP WHERE LOOKUP_KEY = 'CAR_MFG'", which will return a list like: Toyota, Ford, Nissan, Honda, VW, BMW, ...
If I have > 1 lookup properties, how I can speciafy them? LookupDataTemplate1, LookupDataTemplate2, ..., then how to specify them in XAML Resources. and all DataTemplate are pre-defined in XAML, how I can do it in this case.
similar to EnumToListConverter, How I can create LookupmToListConverter1, LookupmToListConverter2?
Thank you,
|
|
|
|
Mikhail | |
|
Commented on 21.April 2011 |
I run example solution with VS 2010 and .NET 4. It starts but when scrolling I am getting following error:
'' is not a valid value for property 'ClickMode'.
Same for 'FlowDirection', 'HorizontalAlignment, ecc.
|
|
|
|
jones | |
|
Commented on 14.June 2011 |
@Mikhail: I get this Error while starting.
Try this with the Property DependencyPropertyInfo.Value in DependencyPropertyInfo.cs:
set { try { _element.SetValue (_descriptor.DependencyProperty, value); } catch { } }
I suppose the error occures, because the Comboboxes have no item selected...
|
|
|
|
Ajay | |
|
Commented on 15.June 2011 |
Really nice articles
|
|
|
|
|
|