引言
WPF 数据模板化模型为定义数据的表示形式提供了很大的灵活性。WPF 控件有支持自定义数据表示形式的内置功能。首先介绍下如何定义Datatemplate,然后再介绍其他数据模板化功能,例如根据自定义逻辑选择模板和支持显示分层数据。
有关 WPF 样式和模板模型的介绍(例如如何使用 Style 来设置控件的属性),请参见样式设置和模板化主题。
另外,了解Resources也很重要,它实际上是有关使对象(例如,Style 和 DataTemplate)成为可重用对象的内容。有关资源的更多信息,请参见XAML 资源。
数据模板基础
本节包含下列子节。
- 没有 DataTemplate
- 定义简单 DataTemplate
- 将 DataTemplate 创建为资源
- DataType 属性
主界面:
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample"
Title="Introduction to Data Templating Sample">
<Window.Resources>
<local:Tasks x:Key="myTodoList"/> ... </Window.Resources>
<StackPanel>
<TextBlock Name="blah" FontSize="20" Text="My Task List:"/>
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"/> ... </StackPanel>
</Window>
没有 DataTemplate
如果没有 DataTemplate,我们的 ListBox 当前具有如下外观:
由于没有任何特定说明,ListBox 在尝试显示集合中的对象时,默认情况下会调用 ToString。因此,如果 Task 对象重写 ToString 方法,则 ListBox 显示基础集合中每个源对象的字符串表示形式。
例如,如果 Task 类以这种方式重写 ToString 方法,其中 name 是 TaskName 属性的字段:
public override string ToString()
{
return name.ToString();
}
但这样很不灵活,有很多限制。另外,如果要绑定XML的数据,无法重写TOString方法。
定义简单 DataTemplate
这样做的一种方式是将 ListBox 的 ItemTemplate 属性设置为 DataTemplate。 DataTemplate 中指定的内容变成数据对象的可视结构。 以下DataTemplate 相当简单。 我们要给出的说明是:每项显示为 StackPanel 中的三个 TextBlock 元素。 每个 TextBlock 元素绑定到 Task 类的一个属性。
<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>
示例的基础数据是 CLR 对象的一个集合。如果要绑定到 XML 数据,则基本概念相同,但是语法有轻微差异。 例如,不是让 Path=TaskName,而是将 XPath 设置为@TaskName(如果 TaskName 是 XML 节点的特性)。
将 DataTemplate 创建为资源
<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>
现在,您可以将 myTaskTemplate 用作资源,如下面的示例所示:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>
因为 myTaskTemplate 是资源,所以您现在可以在具有采用 DataTemplate 类型的属性的其他控件中使用它。如上所述,对于 ItemsControl 对象(例如 ListBox),它是 ItemTemplate属性。 对于 ContentControl 对象,它是 ContentTemplate 属性。
DataType 属性
DataTemplate 类具有 DataType 属性,该属性非常类似于 Style 类的 TargetType 属性。因此,在上述示例中不需要为 DataTemplate 指定 x:Key,您可以执行以下操作:
<DataTemplate DataType="{x:Type local:Task}">
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
此 DataTemplate 自动应用于所有 Task 对象。 请注意,在这种情况下,x:Key 是隐式设置的。 因此,如果要为此 DataTemplate 分配一个 x:Key 值,则要重写隐式 x:Key 并且不会自动应用 DataTemplate。
如果要将 ContentControl绑定到 Task 对象的集合,则 ContentControl不会自动使用以上 DataTemplate。 这是因为在 ContentControl上绑定需要更多的信息来区分是要绑定到整个集合还是要绑定到单个对象。 如果 ContentControl要跟踪对 ItemsControl类型的选择,您可以将 ContentControl 绑定的 Path 属性设置为“/”以表示您对当前项感兴趣。 有关示例,请参见如何:绑定到集合并基于选择显示信息。 否则,需要通过设置 ContentTemplate 属性显式指定 DataTemplate。
DataType 属性在您拥有不同类型数据对象的 CompositeCollection 时尤其有用。 有关示例,请参见如何:实现 CompositeCollection。
向 DataTemplate 添加更多信息
当前,数据显示了必要的信息,但是还可以显示更多信息。让我们通过添加 Border、Grid 和一些用于描述要显示的数据的 TextBlock 来显示更多信息。
<DataTemplate x:Key="myTaskTemplate">
<Border Name="border" BorderBrush="Aqua" BorderThickness="1"
Padding="5" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Task Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Description:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Priority:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
</Grid>
</Border> ... </DataTemplate>
下面的屏幕快照使用此已修改的 DataTemplate 来显示 ListBox。
我们可以在 ListBox 上将 HorizontalContentAlignment 设置为 Stretch 来确保项的宽度占据整个空间:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"
HorizontalContentAlignment="Stretch"/>
参考
原文见:http://msdn.microsoft.com/zh-cn/library/ms742521(v=vs.110).aspx