引言
书接上回[WPF系列-数据邦定之DataTemplate],本篇介绍如何根据属性切换模板(DataTemplate)
切换模板的两种方式:
- 使用DataTemplateSelector来切换模板
- 使用DataTrigger来实现模板切换。
- 使用Style来是实现模板切换
A
DataTemplateSelector
does not respond toPropertyChange
notifications, so it doesn‘t get re-evaluated when your properties change.The alternative I use is
DataTriggers
that changes theTemplate
based on a property.For example, this will draw all
TaskModel
objects using aContentControl
, and theContentControl.Template
is based on theTaskStatus
property of theTaskModel
使用DataTrigger切换模板
<DataTemplate DataType="{x:Type viewModels:CorePlugViewModel}"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowGridLines="True" > <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Text="{Binding Name}" Margin="4"></TextBlock> <ContentControl x:Name="viewBox" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Image Source="{Binding MapThumbnail}" /> </ContentControl> </Grid> <DataTemplate.Triggers> <DataTrigger Binding="{Binding HasMap}" Value="True"> <Setter Property="Template" TargetName="viewBox"> <Setter.Value> <ControlTemplate TargetType="{x:Type ContentControl}"> <views:CorePlugWithMap VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinHeight="10"/> </ControlTemplate> </Setter.Value> </Setter> </DataTrigger> </DataTemplate.Triggers> </DataTemplate>
使用DataTemplateSelector切换模板
在 DataType 属性一节中,我们讨论了您可以针对不同的数据对象定义不同的数据模板。 这在您拥有不同类型的 CompositeCollection 或不同类型的项集合时尤其有用。 在使用 DataTrigger 来应用属性值一节中,我们演示了如果您拥有相同类型的数据对象集合,您可以创建 DataTemplate,然后根据每个数据对象的属性值使用触发器来应用更改。 虽然触发器允许您应用属性值或启动动画,但是它们无法让您灵活重构数据对象的结构。 在某些情况下,可能需要您为类型相同但属性不同的数据对象创建其他 DataTemplate。
例如,当 Task 对象的 Priority 值为 1 时,您可能需要为它指定完全不同的外观,以给予您自己一个提醒。 在这种情况下,您需要创建 DataTemplate 来显示高优先级的 Task 对象。 让我们将以下 DataTemplate 添加到资源部分:
<DataTemplate x:Key="importantTaskTemplate"> <DataTemplate.Resources> <Style TargetType="TextBlock"> <Setter Property="FontSize" Value="20"/> </Style> </DataTemplate.Resources> <Border Name="border" BorderBrush="Red" BorderThickness="1" Padding="5" Margin="5"> <DockPanel HorizontalAlignment="Center"> <TextBlock Text="{Binding Path=Description}" /> <TextBlock>!</TextBlock> </DockPanel> </Border> </DataTemplate>
请注意,此示例使用 DataTemplate.Resources 属性。 DataTemplate 中的元素共享该部分中定义的资源。
若要提供逻辑以根据数据对象的 Priority 值选择要使用的 DataTemplate,需要创建 DataTemplateSelector 的子类并重写 SelectTemplate 方法。 在下面的示例中,SelectTemplate 方法提供逻辑以根据 Priority 属性的值返回适当的模板。 可以在封装 Window 元素的资源中找到要返回的模板。
using System.Windows; using System.Windows.Controls; namespace SDKSample { public class TaskListDataTemplateSelector : DataTemplateSelector { public override DataTemplate SelectTemplate(object item, DependencyObject container) { FrameworkElement element = container as FrameworkElement; if (element != null && item != null && item is Task) { Task taskitem = item as Task; if (taskitem.Priority == 1) return element.FindResource("importantTaskTemplate") as DataTemplate; else return element.FindResource("myTaskTemplate") as DataTemplate; } return null; } } }
然后,我们可以将 TaskListDataTemplateSelector 声明为资源:
<Window.Resources> ... <local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/> ... </Window.Resources>
若要使用模板选择器资源,请将其分配到 ListBox 的 ItemTemplateSelector 属性。 ListBox 为基础集合中的每一项调用 TaskListDataTemplateSelector 的 SelectTemplate 方法。 该调用会将数据对象作为项参数来传递。 然后,将由该方法返回的 DataTemplate 应用于该数据对象。
<ListBox Width="400" Margin="10" ItemsSource="{Binding Source={StaticResource myTodoList}}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}" HorizontalContentAlignment="Stretch"/>
使用模板选择器后,ListBox 现在如下所示:
这正是此示例要得到的结果。有关完整示例,请参见 Introduction to Data Templating Sample(数据模板化简介示例)
使用Style切换模板
<DataTemplate x:Key="personTemplate" TargetType="{x:Type local:TaskModel}"> <TextBlock Text="I‘m an Open Task" /> </DataTemplate> <DataTemplate x:Key="companyTemplate" TargetType="{x:Type local:TaskModel}"> <TextBlock Text="I‘m a Closed Task" /> </DataTemplate> <DataTemplate DataType="{x:Type local:TaskModel}"><ContentControl Content="{Binding}"> <ContentControl.Style> <Style TargetType="ContentControl"> <Style.Triggers> <DataTrigger Binding="{Binding AccountType}" Value="Person"> <Setter Property="ContentTemplate" Value="{StaticResource personTemplate}" /> </DataTrigger> <DataTrigger Binding="{Binding AccountType}" Value="Company"> <Setter Property="ContentTemplate" Value="{StaticResource companyTemplate}" /> </DataTrigger> </Style.Triggers> </Style> </ContentControl.Style> </ContentControl> </DataTemplate>
上面这个例子没有给一个默认的ContentTemplate,我们可以改为:
<DataTemplate x:Key="OpenTaskTemplate" TargetType="{x:Type local:TaskModel}"> <TextBlock Text="I‘m an Open Task" /> </DataTemplate> <DataTemplate x:Key="ClosedTaskTemplate" TargetType="{x:Type local:TaskModel}"> <TextBlock Text="I‘m a Closed Task" /> </DataTemplate> <DataTemplate DataType="{x:Type local:TaskModel}"> <ContentControl Content="{Binding }"> <ContentControl.Style> <Style TargetType="{x:Type ContentControl}"> <!-- Default Template --> <Setter Property="ContentTemplate" Value="{StaticResource OpenTaskTemplate}" /> <!-- Triggers to change Template --> <Style.Triggers> <DataTrigger Binding="{Binding TaskStatus}" Value="Closed"> <Setter Property="ContentTemplate" Value="{StaticResource ClosedTaskTemplate}" /> </DataTrigger> </Style.Triggers> </Style> </ContentControl.Style> </ContentControl> </DataTemplate>
比较
方式 | 优点 | 缺点 |
DataTriger | ||
StyleTriger | ||
DataTemplateSelector | 无法相应propertyChanged事件
|