数据模板化概述Data Templating Overview

WPF 数据模板化模型为定义数据的表示提供了很大的灵活性。The WPF data templating model provides you with great flexibility to define the presentation of your data. WPF 控件具有支持自定义数据表示的内置功能。WPF controls have built-in functionality to support the customization of data presentation. 本主题首先演示如何定义 DataTemplate,然后引入其他数据模板化功能,如基于自定义逻辑的模板选择和对分层数据的显示的支持。This topic first demonstrates how to define a DataTemplate and then introduces other data templating features, such as the selection of templates based on custom logic and the support for the display of hierarchical data.

PrerequisitesPrerequisites

本主题重点介绍数据模板化功能,不介绍数据绑定概念。This topic focuses on data templating features and is not an introduction of data binding concepts. 有关基本数据绑定概念的信息,请参阅数据绑定概述For information about basic data binding concepts, see the Data Binding Overview.

DataTemplate 是数据的表示形式,是 WPF 样式设置和模板化模型所提供的众多功能之一。DataTemplate is about the presentation of data and is one of the many features provided by the WPF styling and templating model. 如要介绍 WPF 样式设置和模板化模型(例如如何使用 Style 设置控件的属性),请参阅样式设置和模板化主题。For an introduction of the WPF styling and templating model, such as how to use a Style to set properties on controls, see the Styling and Templating topic.

此外,请务必了解 Resources,这一点实质上使对象(如 StyleDataTemplate)可重复使用。In addition, it is important to understand Resources, which are essentially what enable objects such as Style and DataTemplate to be reusable. 有关资源的详细信息,请参阅 XAML 资源For more information on resources, see XAML Resources.

数据模板化基础知识Data Templating Basics

为了演示 DataTemplate 很重要的原因,让我们来演练一个数据绑定示例。To demonstrate why DataTemplate is important, let's walk through a data binding example. 在此示例中,我们有一个绑定到 Task 对象列表的 ListBoxIn this example, we have a ListBox that is bound to a list of Task objects. 每个 Task 对象都有 TaskName (string)、Description (string)、Priority (int) 和 TaskType 类型的属性(它是一个 Enum,其值为 HomeWork)。Each Task object has a TaskName (string), a Description (string), a Priority (int), and a property of type TaskType, which is an Enum with values Home and Work.

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

不提供 DataTemplateWithout a DataTemplate

如果没有 DataTemplate,则 ListBox 当前如下所示:Without a DataTemplate, our ListBox currently looks like this:

数据模板化示例屏幕快照Data templating sample screenshot

发生的情况是,在没有任何特定说明的情况下,ListBox 默认情况下,在尝试显示集合中的对象时,会调用 ToStringWhat's happening is that without any specific instructions, the ListBox by default calls ToString when trying to display the objects in the collection. 因此,如果 Task 对象重写 ToString 方法,则 ListBox 会显示基础集合中每个源对象的字符串表示形式。Therefore, if the Task object overrides the ToString method, then the ListBox displays the string representation of each source object in the underlying collection.

例如,如果 Task 类以这种方式重写 ToString 方法,其中 nameTaskName 属性的字段:For example, if the Task class overrides the ToString method this way, where name is the field for the TaskName property:

public override string ToString()
{
    return name.ToString();
}
Public Overrides Function ToString() As String
    Return _name.ToString()
End Function

然后 ListBox 如下所示:Then the ListBox looks like the following:

数据模板化示例屏幕快照Data templating sample screenshot

但是,这会受到限制且不灵活。However, that is limiting and inflexible. 此外,如果您要绑定到 XML 数据,您将无法重写 ToStringAlso, if you are binding to XML data, you wouldn't be able to override ToString.

定义简单的 DataTemplateDefining a Simple DataTemplate

解决方法是定义 DataTemplateThe solution is to define a DataTemplate. 实现此目的的一种方法是将 ListBoxItemTemplate 属性设置为 DataTemplateOne way to do that is to set the ItemTemplate property of the ListBox to a DataTemplate. 您在 DataTemplate 中指定的内容将成为您的数据对象的可视结构。What you specify in your DataTemplate becomes the visual structure of your data object. 以下 DataTemplate 非常简单。The following DataTemplate is fairly simple. 我们将提供每个项在 StackPanel中显示为三个 TextBlock 元素的说明。We are giving instructions that each item appears as three TextBlock elements within a StackPanel. 每个 TextBlock 元素都绑定到 Task 类的属性。Each TextBlock element is bound to a property of the Task class.

<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 对象的集合。The underlying data for the examples in this topic is a collection of CLR objects. 如果要绑定到 XML 数据,基本概念是相同的,但有一些细微的差别。If you are binding to XML data, the fundamental concepts are the same, but there is a slight syntactic difference. 例如,将 XPath 设置为 @TaskName (如果 TaskName 是 XML 节点的属性),而不是让 Path=TaskNameFor example, instead of having Path=TaskName, you would set XPath to @TaskName (if TaskName is an attribute of your XML node).

现在,我们的 ListBox 如下所示:Now our ListBox looks like the following:

数据模板化示例屏幕快照Data templating sample screenshot

将 DataTemplate 创建为资源Creating the DataTemplate as a Resource

在上面的示例中,我们定义了内联 DataTemplateIn the above example, we defined the DataTemplate inline. 常常在资源部分中定义它,以使其成为一个可重复使用的对象,如以下示例所示:It is more common to define it in the resources section so it can be a reusable object, as in the following example:

<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 用作资源,如以下示例所示:Now you can use myTaskTemplate as a resource, as in the following example:

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplate="{StaticResource myTaskTemplate}"/>

由于 myTaskTemplate 是资源,因此你现在可以在具有采用 DataTemplate 类型的属性的其他控件上使用它。Because myTaskTemplate is a resource, you can now use it on other controls that have a property that takes a DataTemplate type. 如上所示,对于 ItemsControl 对象,如 ListBox,它是 ItemTemplate 属性。As shown above, for ItemsControl objects, such as the ListBox, it is the ItemTemplate property. 对于 ContentControl 对象,它是 ContentTemplate 属性。For ContentControl objects, it is the ContentTemplate property.

DataType 属性The DataType Property

DataTemplate 类的 DataType 属性与 Style 类的 TargetType 属性非常相似。The DataTemplate class has a DataType property that is very similar to the TargetType property of the Style class. 因此,可以执行以下操作,而不是在上面的示例中为 DataTemplate 指定 x:KeyTherefore, instead of specifying an x:Key for the DataTemplate in the above example, you can do the following:

<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 对象。This DataTemplate gets applied automatically to all Task objects. 请注意,在这种情况下,隐式设置 x:KeyNote that in this case the x:Key is set implicitly. 因此,如果将此 DataTemplate x:Key 值,则将重写隐式 x:Key,并且不会自动应用 DataTemplateTherefore, if you assign this DataTemplate an x:Key value, you are overriding the implicit x:Key and the DataTemplate would not be applied automatically.

如果要将 ContentControl 绑定到 Task 对象的集合,ContentControl 不会自动使用以上 DataTemplateIf you are binding a ContentControl to a collection of Task objects, the ContentControl does not use the above DataTemplate automatically. 这是因为 ContentControl 上的绑定需要更多的信息来区分你是要绑定到整个集合还是单个对象。This is because the binding on a ContentControl needs more information to distinguish whether you want to bind to an entire collection or the individual objects. 如果 ContentControl 跟踪 ItemsControl 类型的选择,则可以将 ContentControl 绑定的 Path 属性设置为 "/",以指示你对当前项感兴趣。If your ContentControl is tracking the selection of an ItemsControl type, you can set the Path property of the ContentControl binding to "/" to indicate that you are interested in the current item. 有关示例,请参阅绑定到集合并基于选择显示信息For an example, see Bind to a Collection and Display Information Based on Selection. 否则,需要通过设置 ContentTemplate 属性显式指定 DataTemplateOtherwise, you need to specify the DataTemplate explicitly by setting the ContentTemplate property.

当您具有不同类型的数据对象的 CompositeCollection 时,DataType 属性特别有用。The DataType property is particularly useful when you have a CompositeCollection of different types of data objects. 有关示例,请参阅实现 CompositeCollectionFor an example, see Implement a CompositeCollection.

向 DataTemplate 添加更多信息Adding More to the DataTemplate

当前,数据显示了必要信息,但还可以显示更多信息。Currently the data appears with the necessary information, but there's definitely room for improvement. 接下来,我们将添加一个 Border、一个 Grid和一些 TextBlock 元素,这些元素描述正在显示的数据。Let's improve on the presentation by adding a Border, a Grid, and some TextBlock elements that describe the data that is being displayed.


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

以下屏幕截图显示了此修改 DataTemplateListBoxThe following screenshot shows the ListBox with this modified DataTemplate:

数据模板化示例屏幕快照Data templating sample screenshot

我们可以将 HorizontalContentAlignment 设置为 StretchListBox 上,以确保项的宽度占用整个空间:We can set HorizontalContentAlignment to Stretch on the ListBox to make sure the width of the items takes up the entire space:

<ListBox Width="400" Margin="10"
     ItemsSource="{Binding Source={StaticResource myTodoList}}"
     ItemTemplate="{StaticResource myTaskTemplate}" 
     HorizontalContentAlignment="Stretch"/>

HorizontalContentAlignment 属性设置为 Stretch后,ListBox 现在如下所示:With the HorizontalContentAlignment property set to Stretch, the ListBox now looks like this:

数据模板化示例屏幕快照Data templating sample screenshot

使用 DataTriggers 应用属性值Use DataTriggers to Apply Property Values

当前表示形式并未指出 Task 是家庭任务还是办公室任务。The current presentation does not tell us whether a Task is a home task or an office task. 请记住,Task 对象具有类型为 TaskTypeTaskType 属性(该属性是一个枚举,其值为 HomeWork)。Remember that the Task object has a TaskType property of type TaskType, which is an enumeration with values Home and Work.

在下面的示例中,DataTrigger 将名为 border 的元素的 BorderBrush 设置为 Yellow (如果 TaskType 属性为 TaskType.Home)。In the following example, the DataTrigger sets the BorderBrush of the element named border to Yellow if the TaskType property is TaskType.Home.

<DataTemplate x:Key="myTaskTemplate">
<DataTemplate.Triggers>
  <DataTrigger Binding="{Binding Path=TaskType}">
    <DataTrigger.Value>
      <local:TaskType>Home</local:TaskType>
    </DataTrigger.Value>
    <Setter TargetName="border" Property="BorderBrush" Value="Yellow"/>
  </DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

应用程序现在如下所示。Our application now looks like the following. 家庭任务的边界显示为黄色,办公室任务的边界显示为浅绿色:Home tasks appear with a yellow border and office tasks appear with an aqua border:

数据模板化示例屏幕快照Data templating sample screenshot

在此示例中,DataTrigger 使用 Setter 设置属性值。In this example the DataTrigger uses a Setter to set a property value. 触发器类还具有 EnterActionsExitActions 属性,可用于启动一组操作,例如动画。The trigger classes also have the EnterActions and ExitActions properties that allow you to start a set of actions such as animations. 此外,还有一个 MultiDataTrigger 类,可用于基于多个数据绑定属性值应用更改。In addition, there is also a MultiDataTrigger class that allows you to apply changes based on multiple data-bound property values.

实现相同效果的另一种方法是将 BorderBrush 属性绑定到 TaskType 属性,并使用值转换器根据 TaskType 值返回颜色。An alternative way to achieve the same effect is to bind the BorderBrush property to the TaskType property and use a value converter to return the color based on the TaskType value. 就性能而言,使用转换器实现上述效果的效率要高一些。Creating the above effect using a converter is slightly more efficient in terms of performance. 另外,创建自己的转换器可以提供更多灵活性,因为提供了自己的逻辑。Additionally, creating your own converter gives you more flexibility because you are supplying your own logic. 最后,选择使用何种技术取决于当时的具体情况和偏好。Ultimately, which technique you choose depends on your scenario and your preference. 有关如何编写转换器的信息,请参阅 IValueConverterFor information about how to write a converter, see IValueConverter.

DataTemplate 中有哪些内容?What Belongs in a DataTemplate?

在上面的示例中,我们使用 DataTemplate.Triggers 属性将触发器置于 DataTemplate 中。In the previous example, we placed the trigger within the DataTemplate using the DataTemplate.Triggers property. 触发器的 Setter 设置位于 DataTemplate内的元素(Border 元素)的属性的值。The Setter of the trigger sets the value of a property of an element (the Border element) that is within the DataTemplate. 但是,如果您的 Setters 与之相关的属性不是当前 DataTemplate内的元素的属性,则更适合使用 ListBoxItem 类的 Style 设置属性(如果您绑定的控件是ListBox)。However, if the properties that your Setters are concerned with are not properties of elements that are within the current DataTemplate, it may be more suitable to set the properties using a Style that is for the ListBoxItem class (if the control you are binding is a ListBox). 例如,如果你希望 Trigger 在鼠标指向某个项时对该项的 Opacity 值进行动画处理,则需要在 ListBoxItem 样式中定义触发器。For example, if you want your Trigger to animate the Opacity value of the item when a mouse points to an item, you define triggers within a ListBoxItem style. 有关示例,请参阅样式设置和模板化示例简介For an example, see the Introduction to Styling and Templating Sample.

一般情况下,请记住,DataTemplate 将应用于每个生成的 ListBoxItem (有关其实际应用方式和位置的详细信息,请参阅 ItemTemplate 页)。In general, keep in mind that the DataTemplate is being applied to each of the generated ListBoxItem (for more information about how and where it is actually applied, see the ItemTemplate page.). 您的 DataTemplate 仅涉及数据对象的显示和外观。Your DataTemplate is concerned with only the presentation and appearance of the data objects. 在大多数情况下,显示的所有其他方面(例如,选择项时的外观,或者 ListBox 布局项的方式)不属于 DataTemplate的定义。In most cases, all other aspects of presentation, such as what an item looks like when it is selected or how the ListBox lays out the items, do not belong in the definition of a DataTemplate. 有关示例,请参阅对 ItemsControl 进行样式设置和模板化一节。For an example, see the Styling and Templating an ItemsControl section.

根据数据对象的属性选择 DataTemplateChoosing a DataTemplate Based on Properties of the Data Object

DataType 属性一节中,我们讨论了可为不同的数据对象定义不同的数据模板。In The DataType Property section, we discussed that you can define different data templates for different data objects. 当你的不同类型或集合的 CompositeCollection 具有不同类型的项时,这特别有用。That is especially useful when you have a CompositeCollection of different types or collections with items of different types. 在 "使用 Datatrigger 应用属性值" 部分中,我们已显示,如果你有一个相同类型的数据对象的集合,则可以创建 DataTemplate,然后根据每个数据对象的属性值使用触发器来应用更改。In the Use DataTriggers to Apply Property Values section, we have shown that if you have a collection of the same type of data objects you can create a DataTemplate and then use triggers to apply changes based on the property values of each data object. 虽然触发器允许你应用属性值或启动动画,但是它们无法让你灵活地重构数据对象的结构。However, triggers allow you to apply property values or start animations but they don't give you the flexibility to reconstruct the structure of your data objects. 某些情况下,可能需要为类型相同但具有不同属性的数据对象创建不同 DataTemplateSome scenarios may require you to create a different DataTemplate for data objects that are of the same type but have different properties.

例如,当 Task 对象的 Priority 值为 1 时,可能需要为它指定完全不同的外观,以给予你自己一个提醒。For example, when a Task object has a Priority value of 1, you may want to give it a completely different look to serve as an alert for yourself. 在这种情况下,将为高优先级 Task 对象的显示创建 DataTemplateIn that case, you create a DataTemplate for the display of the high-priority Task objects. 让我们将以下 DataTemplate 添加到 resources 部分:Let's add the following DataTemplate to the resources section:

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

此示例使用system.windows.datatemplate>属性。This example uses the DataTemplate.Resources property. 此部分中定义的资源由 DataTemplate中的元素共享。Resources defined in that section are shared by the elements within the DataTemplate.

若要根据数据对象的 Priority 值提供逻辑来选择要使用的 DataTemplate,请创建 DataTemplateSelector 的子类并重写 SelectTemplate 方法。To supply logic to choose which DataTemplate to use based on the Priority value of the data object, create a subclass of DataTemplateSelector and override the SelectTemplate method. 在下面的示例中,SelectTemplate 方法提供逻辑,以根据 Priority 属性的值返回相应的模板。In the following example, the SelectTemplate method provides logic to return the appropriate template based on the value of the Priority property. 在封装 Window 元素的资源中找到要返回的模板。The template to return is found in the resources of the enveloping Window element.

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;
        }
    }
}

Namespace SDKSample
    Public Class TaskListDataTemplateSelector
        Inherits DataTemplateSelector
        Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate

            Dim element As FrameworkElement
            element = TryCast(container, FrameworkElement)

            If element IsNot Nothing AndAlso item IsNot Nothing AndAlso TypeOf item Is Task Then

                Dim taskitem As Task = TryCast(item, Task)

                If taskitem.Priority = 1 Then
                    Return TryCast(element.FindResource("importantTaskTemplate"), DataTemplate)
                Else
                    Return TryCast(element.FindResource("myTaskTemplate"), DataTemplate)
                End If
            End If

            Return Nothing
        End Function
    End Class
End Namespace

然后,我们可以将 TaskListDataTemplateSelector 声明为资源:We can then declare the TaskListDataTemplateSelector as a resource:

<Window.Resources>
<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
</Window.Resources>

若要使用模板选择器资源,请将其分配给 ListBoxItemTemplateSelector 属性。To use the template selector resource, assign it to the ItemTemplateSelector property of the ListBox. ListBox 为基础集合中的每个项调用 TaskListDataTemplateSelectorSelectTemplate 方法。The ListBox calls the SelectTemplate method of the TaskListDataTemplateSelector for each of the items in the underlying collection. 该调用会将数据对象作为项参数传递。The call passes the data object as the item parameter. 然后,将方法返回 DataTemplate 应用于该数据对象。The DataTemplate that is returned by the method is then applied to that data object.

<ListBox Width="400" Margin="10"
         ItemsSource="{Binding Source={StaticResource myTodoList}}"
         ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
         HorizontalContentAlignment="Stretch"/>

设置模板选择器后,ListBox 现在如下所示:With the template selector in place, the ListBox now appears as follows:

数据模板化示例屏幕快照Data templating sample screenshot

这正是此示例要得到的结果。This concludes our discussion of this example. 有关完整示例,请参阅数据模板化示例简介For the complete sample, see Introduction to Data Templating Sample.

对 ItemsControl 进行样式设置和模板化Styling and Templating an ItemsControl

即使 ItemsControl 不是可以使用 DataTemplate 的唯一控件类型,也是将 ItemsControl 绑定到集合的一个非常常见的方案。Even though the ItemsControl is not the only control type that you can use a DataTemplate with, it is a very common scenario to bind an ItemsControl to a collection. system.windows.datatemplate>部分中,我们讨论了 DataTemplate 的定义只应涉及数据的显示。In the What Belongs in a DataTemplate section we discussed that the definition of your DataTemplate should only be concerned with the presentation of data. 为了了解何时不适合使用 DataTemplate,了解 ItemsControl提供的不同样式和模板属性是很重要的。In order to know when it is not suitable to use a DataTemplate it is important to understand the different style and template properties provided by the ItemsControl. 以下示例旨在演示上述每个属性的功能。The following example is designed to illustrate the function of each of these properties. 本示例中的 ItemsControl 绑定到与上一示例中相同的 Tasks 集合。The ItemsControl in this example is bound to the same Tasks collection as in the previous example. 为便于演示,本示例中的样式和模板都进行了内联声明。For demonstration purposes, the styles and templates in this example are all declared inline.

<ItemsControl Margin="10"
              ItemsSource="{Binding Source={StaticResource myTodoList}}">
  <!--The ItemsControl has no default visual appearance.
      Use the Template property to specify a ControlTemplate to define
      the appearance of an ItemsControl. The ItemsPresenter uses the specified
      ItemsPanelTemplate (see below) to layout the items. If an
      ItemsPanelTemplate is not specified, the default is used. (For ItemsControl,
      the default is an ItemsPanelTemplate that specifies a StackPanel.-->
  <ItemsControl.Template>
    <ControlTemplate TargetType="ItemsControl">
      <Border BorderBrush="Aqua" BorderThickness="1" CornerRadius="15">
        <ItemsPresenter/>
      </Border>
    </ControlTemplate>
  </ItemsControl.Template>
  <!--Use the ItemsPanel property to specify an ItemsPanelTemplate
      that defines the panel that is used to hold the generated items.
      In other words, use this property if you want to affect
      how the items are laid out.-->
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <!--Use the ItemTemplate to set a DataTemplate to define
      the visualization of the data objects. This DataTemplate
      specifies that each data object appears with the Proriity
      and TaskName on top of a silver ellipse.-->
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <DataTemplate.Resources>
        <Style TargetType="TextBlock">
          <Setter Property="FontSize" Value="18"/>
          <Setter Property="HorizontalAlignment" Value="Center"/>
        </Style>
      </DataTemplate.Resources>
      <Grid>
        <Ellipse Fill="Silver"/>
        <StackPanel>
          <TextBlock Margin="3,3,3,0"
                     Text="{Binding Path=Priority}"/>
          <TextBlock Margin="3,0,3,7"
                     Text="{Binding Path=TaskName}"/>
        </StackPanel>
      </Grid>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
  <!--Use the ItemContainerStyle property to specify the appearance
      of the element that contains the data. This ItemContainerStyle
      gives each item container a margin and a width. There is also
      a trigger that sets a tooltip that shows the description of
      the data object when the mouse hovers over the item container.-->
  <ItemsControl.ItemContainerStyle>
    <Style>
      <Setter Property="Control.Width" Value="100"/>
      <Setter Property="Control.Margin" Value="5"/>
      <Style.Triggers>
        <Trigger Property="Control.IsMouseOver" Value="True">
          <Setter Property="Control.ToolTip"
                  Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                          Path=Content.Description}"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </ItemsControl.ItemContainerStyle>
</ItemsControl>

下面是该示例在呈现时的屏幕快照:The following is a screenshot of the example when it is rendered:

System.windows.controls.itemscontrol> 示例屏幕快照ItemsControl example screenshot

请注意,您可以使用 ItemTemplateSelector,而不是使用 ItemTemplateNote that instead of using the ItemTemplate, you can use the ItemTemplateSelector. 请参考上一节中的示例。Refer to the previous section for an example. 同样,您可以选择使用 ItemContainerStyleSelector,而不是使用 ItemContainerStyleSimilarly, instead of using the ItemContainerStyle, you have the option to use the ItemContainerStyleSelector.

此处未显示的 ItemsControl 的两个其他样式相关属性 GroupStyleGroupStyleSelectorTwo other style-related properties of the ItemsControl that are not shown here are GroupStyle and GroupStyleSelector.

对分层数据的支持Support for Hierarchical Data

到目前为止,我们仅讨论了如何绑定到并显示单个集合。So far we have only looked at how to bind to and display a single collection. 某些时候,具有的集合包含其他集合。Sometimes you have a collection that contains other collections. HierarchicalDataTemplate 类旨在与 HeaderedItemsControl 类型一起用于显示此类数据。The HierarchicalDataTemplate class is designed to be used with HeaderedItemsControl types to display such data. 在以下示例中,ListLeagueListLeague 对象的列表。In the following example, ListLeagueList is a list of League objects. 每个 League 对象都有一个 NameDivision 对象的集合。Each League object has a Name and a collection of Division objects. 每个 Division 都有一个 NameTeam 对象的集合,并且每个 Team 对象都有一个 NameEach Division has a Name and a collection of Team objects, and each Team object has a Name.

<Window x:Class="SDKSample.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="HierarchicalDataTemplate Sample"
  xmlns:src="clr-namespace:SDKSample">
  <DockPanel>
    <DockPanel.Resources>
      <src:ListLeagueList x:Key="MyList"/>

      <HierarchicalDataTemplate DataType    = "{x:Type src:League}"
                                ItemsSource = "{Binding Path=Divisions}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>

      <HierarchicalDataTemplate DataType    = "{x:Type src:Division}"
                                ItemsSource = "{Binding Path=Teams}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>

      <DataTemplate DataType="{x:Type src:Team}">
        <TextBlock Text="{Binding Path=Name}"/>
      </DataTemplate>
    </DockPanel.Resources>

    <Menu Name="menu1" DockPanel.Dock="Top" Margin="10,10,10,10">
        <MenuItem Header="My Soccer Leagues"
                  ItemsSource="{Binding Source={StaticResource MyList}}" />
    </Menu>

    <TreeView>
      <TreeViewItem ItemsSource="{Binding Source={StaticResource MyList}}" Header="My Soccer Leagues" />
    </TreeView>

  </DockPanel>
</Window>

该示例演示了使用 HierarchicalDataTemplate,可以轻松地显示包含其他列表的列表数据。The example shows that with the use of HierarchicalDataTemplate, you can easily display list data that contains other lists. 下面是该示例的一个屏幕快照。The following is a screenshot of the example.

HierarchicalDataTemplate 示例屏幕快照HierarchicalDataTemplate sample screenshot

请参阅See also