数据模板选择:基于项的属性设置项的样式

集合控件的自定义设计由 DataTemplate 管理。 数据模板定义每个项的布局和样式,并将标记应用于集合中的每个项。 本文介绍如何使用 DataTemplateSelector 在集合上应用不同的数据模板,并根据所选的特定项属性或值选择要使用的数据模板。

重要的 API:DataTemplateSelectorDataTemplate

DataTemplateSelector 是启用自定义模板选择逻辑的类。 它可让你定义规则,以指定要用于集合中特定项的数据模板。 若要实现此逻辑,请在代码隐藏中创建 DataTemplateSelector 的子类,并定义逻辑,以确定要用于特定项类别的数据模板(例如,特定类型的项或具有特定属性值的项等)。 在 XAML 文件的 Resources 节中声明此类的实例,以及要使用的数据模板的定义。 使用 x:Key 值标识这些资源,从而使你可以在 XAML 中引用它们。

先决条件

何时不使用 DataTemplateSelector

通常,不应为 ListView 或 GridView 中的每个项都指定完全不同的布局/样式,这是对 DataTemplateSelector 的不当使用,且会对性能产生负面影响。

通过绑定某些属性,可以仅使用一个数据模板来控制列表项视觉对象显示的某些元素。 例如,通过绑定到数据模板中的图标源属性,并为每个项赋予不同的图标源属性值,每个项都可以具有不同的图标。 与使用 DataTemplateSelector 相比,这将获得更好的性能。

何时使用 DataTemplateSelector

若要在一个集合控件中使用多个数据模板,则应创建一个 DataTemplateSelectorDataTemplateSelector 使你可以灵活地使某些项突出显示,同时仍将项保持在相似的布局中。 在许多用例中,DataTemplateSelector 很有帮助,而在某些情况下,最好重新考虑所使用的控件和策略。

集合控件通常绑定到所有项均为一种类型的集合。 但是,即使项可能为同一类型,它们也可能对某些属性具有不同的值,或表示不同的含义。 某些项目也可能比其他项更重要,或者一个项可能特别重要或不同,并且需要在视觉上突出显示。在这些情况下,DataTemplateSelector 将非常有用。

此外,还可以绑定到包含不同类型的项的集合 - 绑定集合可以混合使用字符串、整数、自定义类对象等。 这使得 DataTemplateSelector 尤其有用,因为它可以根据项的对象类型分配不同的数据模板。

以下是何时可以使用数据模板选择器的一些示例:

  • 在 ListView 内表示不同级别的员工 - 每种类型/级别的员工可能需要不同的颜色背景以轻松区分。
  • 在使用 GridView 的产品库中表示促销商品 - 某个促销商品可能需要红色背景或不同的颜色字体,以使其较正价商品更加醒目。
  • 在使用 FlipView 的图片库中表示获奖者/最佳照片。
  • 需要在 ListView 中以不同方式表示负数/正数,或短字符串/长字符串。

创建 DataTemplateSelector

创建数据模板选择器时,请在代码中定义模板选择逻辑,并在 XAML 中定义数据模板。

代码隐藏组件

若要使用数据模板选择器,请首先在代码隐藏中创建 DataTemplateSelector 的子类(从其派生的类)。 在类中,将每个模板声明为类的一个属性。 然后,替代 SelectTemplateCore 方法以加入自己的模板选择逻辑。

下面是名为 MyDataTemplateSelector 的简单 DataTemplateSelector 子类的示例。

public class MyDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate Normal { get; set; }
    public DataTemplate Accent { get; set; }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        if ((int)item % 2 == 0)
        {
            return Normal;
        }
        else
        {
            return Accent;
        }
    }
}

MyDataTemplateSelector 类派生自 DataTemplateSelector 类,并首先定义两个 DataTemplate 对象:NormalAccent。 这些暂时是空声明,但将在 XAML 文件中使用标记“填充”。

SelectTemplateCore 方法采用项对象(即每个集合项),并使用在特定情况下 DataTemplate 要返回的特定规则进行替代。 在这种情况下,如果项为奇数,则它将接收 Accent 数据模板,如果为偶数,则接收 Normal 数据模板。

XAML 组件

其次,必须在 XAML 文件的 Resources 节中创建此新 MyDataTemplateSelector 类的实例。 所有资源都需要 x:Key,使用它以将其绑定到集合控件的 ItemTemplateSelector 属性(在后续步骤中)。 此外,还要创建两个 DataTemplate 对象的实例,并在 Resources 节中定义其布局。 将这些数据模板分配给在 MyDataTemplateSelector 类中声明了的 AccentNormal 属性。

以下是所需 XAML 资源和标记的示例:

<Page.Resources>

<DataTemplate x:Key="NormalItemTemplate" x:DataType="x:Int32">
    <Button HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource SystemChromeLowColor}">
        <TextBlock Text="{x:Bind}" />
    </Button>
</DataTemplate>

<DataTemplate x:Key="AccentItemTemplate" x:DataType="x:Int32">
    <Button HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource SystemAccentColor}">
        <TextBlock Text="{x:Bind}" />
    </Button>
</DataTemplate>

<l:MyDataTemplateSelector x:Key="MyDataTemplateSelector"
    Normal="{StaticResource NormalItemTemplate}"
    Accent="{StaticResource AccentItemTemplate}"/>

</Page.Resources>

如上所示,定义了两个数据模板 NormalAccent - 它们都将项显示为按钮,但是,Accent 数据模板针对背景使用主题色画笔,而 Normal 数据模板使用灰色画笔 (SystemChromeLowColor)。 然后,将这两个数据模板分配给 NormalAccent DataTemplate 对象,这些对象是在 C# 代码隐藏中创建的 MyDataTemplateSelector 类的属性。

最后一步是将 DataTemplateSelector 绑定到集合控件(在本例中为 ListView)的 ItemTemplateSelector 属性。 从而无需为 ItemTemplate 属性赋值。

<ListView x:Name = "TestListView"
          ItemsSource = "{x:Bind NumbersList}"
          ItemTemplateSelector = "{StaticResource MyDataTemplateSelector}">
</ListView>

代码编译完成后,每个集合项都将通过 MyDataTemplateSelector 中已替代的 SelectTemplateCore 方法运行,并将呈现为相应的 DataTemplate。

重要

DataTemplateSelectorItemsRepeater 结合使用时,可将 DataTemplateSelector 绑定到 ItemTemplate 属性。 ItemsRepeater 没有 ItemTemplateSelector 属性。

DataTemplateSelector 性能注意事项

将 ListView 或 GridView 与大型数据集合结合使用时,滚动和平移性能可能是一个问题。 为保持大型集合的良好运行,可采取一些步骤来提高数据模板的性能。 ListView 和 GridView UI 优化中更详细地介绍了这些内容。

  • 按项目减少元素 - 将数据模板中的 UI 元素数保持在合理的最小值。
  • 使用异类集合进行容器回收
    • 使用 ChoosingItemContainer 事件 - 此事件是一种针对不同项使用不同数据模板的高性能方法。 若要获得最佳性能,应优化缓存并为特定数据选择数据模板。
    • 使用项模板选择器DataTemplateSelector - 在某些实例中,应避免使用项模板选择器 (),因为它会影响性能。