ListView 和 GridView 控件管理其项的排列方式(水平、垂直、包装等),以及用户如何与项交互,但不能管理单个项在屏幕上的显示方式。 项可视化由项容器管理。 将项添加到列表视图时,它们会自动放置在容器中。 ListView 的默认项容器为 ListViewItem;对于 GridView,它是 GridViewItem。
重要 API:ListView 类、GridView 类、ListViewItem 类、GridViewItem 类、ItemTemplate 属性、ItemContainerStyle 属性
注释
ListView 和 GridView 都派生自 ListViewBase 类,因此它们具有相同的功能,但以不同的方式显示数据。 在本文中,当我们讨论列表视图时,除非另行指定,否则信息将同时应用于 ListView 和 GridView 控件。 我们可能会引用 ListView 或 ListViewItem 等类,但 List 前缀可使用相应网格等效项(GridView 或 GridViewItem)的 Grid 代替。
列表视图项和网格视图项
如上所述,ListView 项会自动放置在 ListViewItem 容器中,GridView 项放置在 GridViewItem 容器中。 这些项目容器是自带样式和交互功能的控件,但也可以进行高度定制。 但是,在自定义之前,请确保了解与 ListViewItem 和 GridViewItem 相关的建议样式和准则。
- ListViewItems - 项目主要以文本为主,形状较长。 图标或图像可能显示在文本左侧。
- GridViewItems - 项通常为正方形形状,或者至少不是长方形矩形形状。 项目以图像为中心,更可能包括在图像周围出现或覆盖在图像上的文本。
定制化介绍
容器控件(如 ListViewItem 和 GridViewItem)由两个重要部分组成,用于创建为项显示的最终视觉对象: 数据模板 和 控件模板。
- 数据模板 - 将 DataTemplate 分配给列表视图的 ItemTemplate 属性,以指定各个数据项的显示方式。
- 控件模板 - 控件模板提供由框架负责的可视化部分,例如视觉状态。 可以使用 ItemContainerStyle 属性修改控件模板。 通常,可以修改列表视图颜色以匹配品牌,或更改所选项的显示方式。
此图显示了控件模板和数据模板如何组合以创建项的最终视觉对象。
下面是创建此项的 XAML。 稍后我们将介绍模板。
<ListView Width="220" SelectionMode="Multiple">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<Grid Background="Yellow">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="54"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="Assets/placeholder.png" Width="44" Height="44"
HorizontalAlignment="Left"/>
<TextBlock Text="{x:Bind}" Foreground="Black"
FontSize="14" Grid.Column="1"
VerticalAlignment="Center"
Padding="0,0,54,0"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="LightGreen"/>
</Style>
</ListView.ItemContainerStyle>
<x:String>Item 1</x:String>
<x:String>Item 2</x:String>
<x:String>Item 3</x:String>
<x:String>Item 4</x:String>
<x:String>Item 5</x:String>
</ListView>
重要
数据模板和控件模板用于自定义 ListView 和 GridView 以外的许多控件的样式。 其中包括具有其自己的内置样式的控件,如 FlipView 和自定义创建的控件,如 ItemsRepeater。 虽然以下示例特定于 ListView/GridView,但概念可以应用于许多其他控件。
先决条件
- 我们假设你知道如何使用列表视图控件。 有关详细信息,请参阅 ListView 和 GridView 文章。
- 我们还假设你了解控件样式和模板,包括如何使用内联样式或作为资源。 有关详细信息,请参阅 控件样式 和 控件模板。
数据
在深入了解如何在列表视图中显示数据项之前,我们需要了解要显示的数据。 在此示例中,我们创建一个名为 NamedColor 的数据类型。 它将颜色名称、颜色值和 SolidColorBrush 组合在一起,该颜色公开为 3 个属性: Name、 Color和 Brush。
然后,我们使用 List 填充一个 NamedColor 对象,该对象对应于 Colors 类中的每个命名颜色。 该列表被设定为列表视图的 ItemsSource。
下面是用于定义类并填充 NamedColors 列表的代码。
C#
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
namespace ColorsListApp
{
public sealed partial class MainPage : Page
{
// The list of colors won't change after it's populated, so we use List<T>.
// If the data can change, we should use an ObservableCollection<T> intead.
List<NamedColor> NamedColors = new List<NamedColor>();
public MainPage()
{
this.InitializeComponent();
// Use reflection to get all the properties of the Colors class.
IEnumerable<PropertyInfo> propertyInfos = typeof(Colors).GetRuntimeProperties();
// For each property, create a NamedColor with the property name (color name),
// and property value (color value). Add it the NamedColors list.
for (int i = 0; i < propertyInfos.Count(); i++)
{
NamedColors.Add(new NamedColor(propertyInfos.ElementAt(i).Name,
(Color)propertyInfos.ElementAt(i).GetValue(null)));
}
colorsListView.ItemsSource = NamedColors;
}
}
class NamedColor
{
public NamedColor(string colorName, Color colorValue)
{
Name = colorName;
Color = colorValue;
}
public string Name { get; set; }
public Color Color { get; set; }
public SolidColorBrush Brush
{
get { return new SolidColorBrush(Color); }
}
}
}
数据模板
指定一个数据模板,告知列表视图应如何显示数据项。
在默认情况下,数据项以绑定到的数据对象的字符串表现形式显示在列表视图中。 如果在列表视图中显示“NamedColors”数据而不告知列表视图的外观,则它只显示 ToString 方法返回的任何内容,如下所示。
XAML
<ListView x:Name="colorsListView"/>
可以通过将 DisplayMemberPath 设置为该属性来显示数据项的特定属性的字符串表示形式。 在这里,你需要将 DisplayMemberPath 属性设置为 NamedColor 项的 Name 属性。
XAML
<ListView x:Name="colorsListView" DisplayMemberPath="Name" />
列表视图现在按名称显示项,如下所示。 它更有用,但不够有趣,隐藏了大量信息。
你通常希望更丰富地呈现你的数据。 若要准确指定列表视图中的项的显示方式,请创建 DataTemplate。 DataTemplate 中的 XAML 定义用于显示各项的控件的布局和外观。 该布局中的控件可绑定到数据对象的属性,或者具有在内联中定义的静态内容。 将 DataTemplate 分配给列表控件的 ItemTemplate 属性。
重要
不能同时使用 ItemTemplate 和 DisplayMemberPath 。 如果设置了这两个属性,将发生异常。
在这里,你定义一个 DataTemplate,它根据项的颜色显示矩形,颜色名称和 RGB 值。
注释
在 DataTemplate 中使用 x:Bind 标记扩展 时,必须在 DataTemplate 上指定 DataType (x:DataType)。
XAML
<ListView x:Name="colorsListView">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:NamedColor">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="54"/>
<ColumnDefinition Width="32"/>
<ColumnDefinition Width="32"/>
<ColumnDefinition Width="32"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Width="44" Height="44" Fill="{x:Bind Brush}" Grid.RowSpan="2"/>
<TextBlock Text="{x:Bind Name}" Grid.Column="1" Grid.ColumnSpan="4"/>
<TextBlock Text="{x:Bind Color.R}" Grid.Column="1" Grid.Row="1" Foreground="Red"/>
<TextBlock Text="{x:Bind Color.G}" Grid.Column="2" Grid.Row="1" Foreground="Green"/>
<TextBlock Text="{x:Bind Color.B}" Grid.Column="3" Grid.Row="1" Foreground="Blue"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
下面是使用此数据模板显示数据项时的外观。
重要
默认情况下,ListViewItems 的内容左对齐,即其 HorizontalContentAlignmentProperty 设置为 Left。 如果 ListViewItem 中有多个元素水平相邻,例如水平堆叠的元素或放置在同一网格行中的元素,它们都将左对齐,并且仅用其定义的边距分隔。
若要让元素分散以填充 ListItem 的整个正文,需要使用 ListView 内的 Setter 将 HorizontalContentAlignmentProperty 设置为 Stretch:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
可能需要在 GridView 中显示数据。 下面是另一个数据模板,它以更适合网格布局的方式显示数据。 这次,数据模板被定义为资源,而不是像以前那样内联在 GridView 的 XAML 中。
XAML
<Page.Resources>
<DataTemplate x:Key="namedColorItemGridTemplate" x:DataType="local:NamedColor">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32"/>
<ColumnDefinition Width="32"/>
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="96"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Width="96" Height="96" Fill="{x:Bind Brush}" Grid.ColumnSpan="3" />
<!-- Name -->
<Border Background="#AAFFFFFF" Grid.ColumnSpan="3" Height="40" VerticalAlignment="Top">
<TextBlock Text="{x:Bind Name}" TextWrapping="Wrap" Margin="4,0,0,0"/>
</Border>
<!-- RGB -->
<Border Background="Gainsboro" Grid.Row="1" Grid.ColumnSpan="3"/>
<TextBlock Text="{x:Bind Color.R}" Foreground="Red"
Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center"/>
<TextBlock Text="{x:Bind Color.G}" Foreground="Green"
Grid.Column="1" Grid.Row="1" HorizontalAlignment="Center"/>
<TextBlock Text="{x:Bind Color.B}" Foreground="Blue"
Grid.Column="2" Grid.Row="1" HorizontalAlignment="Center"/>
<!-- HEX -->
<Border Background="Gray" Grid.Row="2" Grid.ColumnSpan="3">
<TextBlock Text="{x:Bind Color}" Foreground="White" Margin="4,0,0,0"/>
</Border>
</Grid>
</DataTemplate>
</Page.Resources>
...
<GridView x:Name="colorsGridView"
ItemTemplate="{StaticResource namedColorItemGridTemplate}"/>
使用此数据模板在网格中显示数据时,如下所示。
性能注意事项
数据模板是定义列表视图外观的主要方式。 如果列表显示大量项目,它们也会对性能产生重大影响。
为列表视图中的每个项创建数据模板中每个 XAML 元素的实例。 例如,上一示例中的网格模板有 10 个 XAML 元素(1 个网格、1 个矩形、3 个边框、5 个 TextBlocks)。 使用此数据模板在屏幕上显示 20 个项目的 GridView 创建至少 200 个元素(20*10=200)。 减少数据模板中的元素数可以大大减少为列表视图创建的元素总数。 有关详细信息,请参阅 ListView 和 GridView UI 优化:每个项的元素计数减少。
请考虑网格数据模板的此部分。 让我们来看一些能够减少元素数量的方法。
XAML
<!-- RGB -->
<Border Background="Gainsboro" Grid.Row="1" Grid.ColumnSpan="3"/>
<TextBlock Text="{x:Bind Color.R}" Foreground="Red"
Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center"/>
<TextBlock Text="{x:Bind Color.G}" Foreground="Green"
Grid.Column="1" Grid.Row="1" HorizontalAlignment="Center"/>
<TextBlock Text="{x:Bind Color.B}" Foreground="Blue"
Grid.Column="2" Grid.Row="1" HorizontalAlignment="Center"/>
- 首先,布局使用单个网格。 可以有单列网格,并将这 3 个 TextBlock 放在 StackPanel 中,但在多次创建的数据模板中,应查找避免在其他布局面板中嵌入布局面板的方法。
- 其次,可以使用边框控件来呈现背景,而无需在 Border 元素中实际放置项目。 Border 元素只能有一个子元素,因此需要添加一个附加布局面板,以在 XAML 的 Border 元素中托管 3 个 TextBlock 元素。 如果不将 TextBlocks 设置为 Border 的子元素,就无需使用面板来容纳 TextBlocks。
- 最后,可以将 TextBlock 放置在 StackPanel 中,并在 StackPanel 上设置边框属性,而不是使用显式边框元素。 但是,Border 元素是一个比 StackPanel 更轻量级的控件,因此在多次呈现时,它对性能的影响较小。
对不同项目使用不同的布局
控件模板
项目的控制模板包含用以显示状态的视觉对象,如选择、指针悬停和焦点。 这些视觉对象呈现在数据模板的顶部或下方。 此处显示了 ListView 控件模板绘制的一些常见默认视觉对象。
- 悬停 – 在数据模板下方绘制的浅灰色矩形。
- 选择 – 在数据模板下方绘制的浅蓝色矩形。
- 键盘焦点 – 在项模板顶部绘制的高 可见性焦点视觉对象 。
列表视图结合了数据模板和控件模板中的元素,以创建在屏幕上呈现的最终视觉对象。 此处,状态视觉对象显示在列表视图的上下文中。
ListViewItemPresenter
如前所述数据模板,为每个项创建的 XAML 元素数可能会对列表视图的性能产生重大影响。 由于数据模板和控件模板组合在一起以显示每个项,因此显示项所需的实际元素数包括这两个模板中的元素。
ListView 和 GridView 控件经过优化,以减少每个项创建的 XAML 元素数。 ListViewItem 视觉对象由 ListViewItemPresenter 创建,该元素是一个特殊的 XAML 元素,用于显示复杂视觉对象以获取焦点、选择和其他视觉状态,而无需大量 UIElement 的开销。
注释
在适用于 Windows 的 UWP 应用中, ListViewItem 和 GridViewItem 都使用 ListViewItemPresenter;GridViewItemPresenter 已弃用,不应使用它。 ListViewItem 和 GridViewItem 在 ListViewItemPresenter 上设置不同的属性值,以实现不同的默认外观。
若要修改项容器的外观,请使用 ItemContainerStyle 属性并提供一个 Style ,其 TargetType 设置为 ListViewItem 或 GridViewItem。
在此示例中,将填充添加到 ListViewItem,以在列表中的项之间创建一些空间。
<ListView x:Name="colorsListView">
<ListView.ItemTemplate>
<!-- DataTemplate XAML shown in previous ListView example -->
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0,4"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
现在,列表视图如下所示,项之间有空格。
在 ListViewItem 默认样式中,ListViewItemPresenter ContentMargin 属性通过 TemplateBinding 与 ListViewItem Padding 属性关联(<ListViewItemPresenter ContentMargin="{TemplateBinding Padding}"/>)。 设置 Padding 属性时,该值实际上将传递给 ListViewItemPresenter ContentMargin 属性。
若要修改未绑定到 ListViewItems 属性的其他 ListViewItemPresenter 属性,需要使用一个新的 ListViewItemPresenter 对 ListViewItem 重新设置模板,并对其属性进行修改。
注释
ListViewItem 和 GridViewItem 默认样式在 ListViewItemPresenter 上设置大量属性。 应始终从默认样式的副本开始,并仅修改所需的属性。 否则,视觉对象可能不会按预期方式显示,因为某些属性未正确设置。
在 Visual Studio 中创建默认模板的副本
- 打开“文档大纲”窗格(查看 > 其他 Windows > 文档大纲)。
- 选择要修改的列表或网格元素。 在此示例中,将修改
colorsGridView元素。 - 右键单击并选择 编辑其他模板 > 编辑生成的项容器(ItemContainerStyle)> 编辑副本。
- 在“创建样式资源”对话框中,输入样式的名称。 在此示例中,你将使用
colorsGridViewItemStyle。
默认样式的副本作为资源添加到您的应用中,并将 GridView.ItemContainerStyle 属性设置为该资源,如 XAML 中所示。
<Style x:Key="colorsGridViewItemStyle" TargetType="GridViewItem">
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="TabNavigation" Value="Local"/>
<Setter Property="IsHoldingEnabled" Value="True"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,0,4,4"/>
<Setter Property="MinWidth" Value="{ThemeResource GridViewItemMinWidth}"/>
<Setter Property="MinHeight" Value="{ThemeResource GridViewItemMinHeight}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<ListViewItemPresenter
CheckBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
ContentMargin="{TemplateBinding Padding}"
CheckMode="Overlay"
ContentTransitions="{TemplateBinding ContentTransitions}"
CheckBoxBrush="{ThemeResource SystemControlBackgroundChromeMediumBrush}"
DragForeground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
DragBackground="{ThemeResource ListViewItemDragBackgroundThemeBrush}"
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
FocusBorderBrush="{ThemeResource SystemControlForegroundAltHighBrush}"
FocusSecondaryBorderBrush="{ThemeResource SystemControlForegroundBaseHighBrush}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
PointerOverForeground="{ThemeResource SystemControlForegroundBaseHighBrush}"
PressedBackground="{ThemeResource SystemControlHighlightListMediumBrush}"
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
PointerOverBackground="{ThemeResource SystemControlHighlightListLowBrush}"
ReorderHintOffset="{ThemeResource GridViewItemReorderHintThemeOffset}"
SelectedPressedBackground="{ThemeResource SystemControlHighlightListAccentHighBrush}"
SelectionCheckMarkVisualEnabled="True"
SelectedForeground="{ThemeResource SystemControlForegroundBaseHighBrush}"
SelectedPointerOverBackground="{ThemeResource SystemControlHighlightListAccentMediumBrush}"
SelectedBackground="{ThemeResource SystemControlHighlightAccentBrush}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<GridView x:Name="colorsGridView" ItemContainerStyle="{StaticResource colorsGridViewItemStyle}"/>
现在可以修改 ListViewItemPresenter 上的属性,以控制视觉状态的选择复选框、项定位和画笔颜色。
内联和覆盖选择视觉对象
ListView 和 GridView 会根据不同的控件和 SelectionMode,以不同的方式来指示所选项。 有关列表视图选择的详细信息,请参阅 ListView 和 GridView。
当 SelectionMode 设置为 “多个”时,选择复选框将显示为项控件模板的一部分。 可以使用 SelectionCheckMarkVisualEnabled 属性关闭多选模式下的选择复选框。 但是,在其他选择模式下忽略此属性,因此无法在“扩展”或“单选模式”中打开复选框。
可以设置 CheckMode 属性以指定是使用内联样式还是覆盖样式显示复选框。
- 内联:此样式显示内容左侧的复选框,并为项目容器的背景设置颜色以指示所选内容。 这是 ListView 的默认样式。
- 覆盖:此样式在内容上方显示复选框,并且仅为项目容器的边框上色以指示选择。 这是 GridView 的默认样式。
此表显示用于指示所选内容的默认视觉对象。
| SelectionMode: | 单一/扩展 | Multiple |
|---|---|---|
| 内联 |
|
|
| 覆盖 |
|
|
注释
在此和以下示例中,将显示简单的字符串数据项,而无需使用数据模板来强调控件模板提供的视觉对象。
还有几种画笔属性可以更改复选框的颜色。 接下来,我们将与其他画笔属性一起查看这些属性。
画笔
许多属性指定用于不同视觉状态的画笔。 你可能想要修改这些内容以匹配品牌的颜色。
下表显示了 ListViewItem 的 Common 和 Selection 视觉状态,以及用于呈现每个状态的视觉对象的画笔。 图像显示画笔对内联和覆盖选择视觉样式的影响。
注释
在此表中,画笔的颜色值是指定的命名颜色,这些颜色被硬编码为固定值,以便更明显地展示它们在模板中的应用位置。 这些不是视觉状态的默认颜色。 如果修改应用中的默认颜色,应像默认模板中那样,使用画笔资源来修改颜色值。
| 状态/画笔名称 | 内联样式 | 覆盖样式 |
|---|---|---|
Normal
|
|
|
PointerOver
|
|
|
按下
|
|
|
选定
|
|
|
PointerOverSelected
|
|
|
PressedSelected
|
|
|
专注
|
|
|
ListViewItemPresenter 还有用于数据占位符和拖动状态的其他画刷属性。 如果在列表视图中使用增量加载或拖放,应考虑是否需要修改这些额外的画笔属性。 有关可以修改的属性的完整列表,请参阅 ListViewItemPresenter 类。
扩展的 XAML 项目模板
如果需要更改复选框的位置(例如)可以使用 ListViewItemExpanded 或 GridViewItemExpanded 模板进行比 ListViewItemPresenter 属性允许的修改更多。 这些模板包含在 generic.xaml 中的默认样式中。 它们遵循标准 XAML 模式,从单个 UIElements 生成所有视觉对象。
如前所述,项模板中的 UIElement 数对列表视图的性能产生了重大影响。 将 ListViewItemPresenter 替换为扩展的 XAML 模板会大大增加元素计数,在列表视图显示大量项目或性能问题时不建议这样做。
注释
仅当列表视图的 ItemsPanel 是 ItemsWrapGrid 或 ItemsStackPanel 时,才支持 ListViewItemPresenter。 如果将 ItemsPanel 更改为使用 VariableSizedWrapGrid、 WrapGrid 或 StackPanel,则项目模板会自动切换到扩展的 XAML 模板。 有关详细信息,请参阅 ListView 和 GridView UI 优化。
若要自定义扩展的 XAML 模板,需要在应用中创建它的副本,并将 ItemContainerStyle 属性设置为副本。
复制扩展的模板
在此处为您的 ListView 或 GridView 设置 ItemContainerStyle 属性。
<ListView ItemContainerStyle="{StaticResource ListViewItemExpanded}"/> <GridView ItemContainerStyle="{StaticResource GridViewItemExpanded}"/>在 Visual Studio 属性窗格中,展开“杂项”部分并查找 ItemContainerStyle 属性。 (确保已选择 ListView 或 GridView。
单击 ItemContainerStyle 属性的属性标记器。 (这是 TextBox 旁边的小框。它被着色为绿色,以表示其设置为 StaticResource。)属性菜单会打开。
在属性菜单中,单击“ 转换为新资源”。
在“创建样式资源”对话框中,输入资源的名称,然后单击“确定”。
在应用中创建了 generic.xaml 扩展模板的副本,你可以根据需要对其进行修改。