首页 > 代码库 > [WPF系列-数据邦定之DataTemplate 根据对象属性切换模板

[WPF系列-数据邦定之DataTemplate 根据对象属性切换模板

 

引言

书接上回[WPF系列-数据邦定之DataTemplate],本篇介绍如何根据属性切换模板(DataTemplate)

 

切换模板的两种方式:

 

  • 使用DataTemplateSelector来切换模板
  • 使用DataTrigger来实现模板切换。
  • 使用Style来是实现模板切换

 

A DataTemplateSelector does not respond to PropertyChange notifications, so it doesn‘t get re-evaluated when your properties change.

The alternative I use is DataTriggers that changes the Template based on a property.

For example, this will draw all TaskModel objects using a ContentControl, and the ContentControl.Template is based on the TaskStatus property of the TaskModel

 

使用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事件

 

 

 

参考

Change Data template dynamically

数据模板化概述

[WPF系列-数据邦定之DataTemplate 根据对象属性切换模板