[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

 

 

 

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

 

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

 

如果您的 Setters 相关属性不是当前 DataTemplate 中元素的属性,则使用 Style(用于 ListBoxItem 类)设置属性更合适(如果您要绑定的控件是 ListBox)。

 

比较

方式 注意事项 适用
DataTemplateSelector 无法相应propertyChanged事件 静态更改模板
StyleTriger   能够动态更改绑定模板
DataTriger Setters 相关属性不是当前 DataTemplate 中元素的属性,则使用 Style 能够动态更改绑定模板

 

 

 

参考

Change Data template dynamically

数据模板化概述

posted @ 2014-12-17 13:57  霍旭东  阅读(2569)  评论(0编辑  收藏  举报