ListView
.NET Multi-platform App UI (.NET MAUI) ListView 显示可选择数据项的可滚动垂直列表。 虽然 ListView 管理列表的外观,但列表中每个项的外观由使用 Cell 显示项的 DataTemplate 定义。 .NET MAUI 包括用于显示文本和图像组合的单元格类型,还可以定义显示所需内容的自定义单元格。 ListView 还支持显示页眉和页脚、分组数据、下拉以刷新和上下文菜单项。
ListView 类派生自 ItemsView<Cell>
类,从其继承以下属性:
ItemsSource
,类型为IEnumerable
,指定要显示的项目集合,其默认值为null
。ItemTemplate
,类型为 DataTemplate,用于指定模板,以应用到要显示的项目集中的各个项。
ListView 定义以下属性:
Footer
,类型为object
,指定将在列表末尾显示的字符串或视图。FooterTemplate
,类型为 DataTemplate,指定用于设置Footer
格式的 DataTemplate。GroupHeaderTemplate
,类型为 DataTemplate,定义用于定义每个组标头外观的 DataTemplate。 此属性与GroupDisplayBinding
属性互斥。 因此,设置此属性会将GroupDisplayBinding
设置为null
。HasUnevenRows
,类型为bool
,指示列表中的项是否可以具有高度不同的行。 此属性的默认值为false
。Header
,类型为object
,指定将在列表开头显示的字符串或视图。HeaderTemplate
,类型为 DataTemplate,指定用于设置Header
格式的 DataTemplate。HorizontalScrollBarVisibility
,类型为ScrollBarVisibility
,指示水平滚动条何时可见。IsGroupedEnabled
,类型为bool
,指示基础数据是否应在组中显示。 此属性的默认值为false
。IsPullToRefreshEnabled
,类型为bool
,指示用户是否可以向下轻扫以使 ListView 刷新其数据。 此属性的默认值为false
。IsRefreshing
,类型为bool
,指示 ListView 是否正在刷新。 此属性的默认值为false
。RefreshCommand
,类型为 ICommand,表示在触发刷新时将执行的命令。RefreshControlColor
,类型为 Color,确定刷新发生时显示的刷新可视化效果的颜色。RowHeight
,类型为int
,确定当HasUnevenRows
为false
时每行的高度。SelectedItem
,类型为object
,表示 ListView 中当前选定的项。SelectionMode
,类型为ListViewSelectionMode
,指示是否可以在 ListView 中选择项。 此属性的默认值为Single
。SeparatorColor
,类型为 Color,定义分隔列表中各项的条的颜色。SeparatorVisibility
,类型为SeparatorVisibility
,定义项之间的分隔符是否可见。VerticalScrollBarVisibility
,类型为ScrollBarVisibility
类型,指示垂直滚动条何时可见。
所有这些属性都由 BindableProperty 对象提供支持,也就是说,可以将它们用作数据绑定的目标,并能对它们进行样式设置。
此外,ListView 还定义了以下不受 BindableProperty 对象支持的属性:
GroupDisplayBinding
,类型为BindingBase
,用于显示组标头的绑定。 此属性与GroupHeaderTemplate
属性互斥。 因此,设置此属性会将GroupHeaderTemplate
设置为null
。GroupShortNameBinding
,类型为BindingBase
,用于在分组跳转列表中显示的名称的绑定。CachingStrategy
,类型为ListViewCachingStrategy
,定义 ListView 的单元格重用策略。 这是只读属性。
ListView 定义以下事件:
ItemAppearing
,在项的视觉对象表示形式添加到 ListView 的视觉对象布局时引发。 此事件随附的ItemVisibilityEventArgs
对象可定义Item
和Index
属性。ItemDisappearing
,在从 ListView 的视觉对象布局中删除项的视觉对象表示形式时引发。 此事件随附的ItemVisibilityEventArgs
对象可定义Item
和Index
属性。ItemSelected
,在选定了列表中的新项时引发。 此事件随附的SelectedItemChangedEventArgs
对象可定义SelectedItem
和SelectedItemIndex
属性。ItemTapped
,在点击 ListView 中的项时引发。 此事件随附的ItemTappedEventArgs
对象可定义Group
、Item
和ItemIndex
属性。Refreshing
,在 ListView 上触发下拉以刷新操作时引发。Scrolled
, . 此事件随附的ScrolledEventArgs
对象可定义ScrollX
和ScrollY
属性:ScrollToRequested
. 此事件随附的ScrollToRequestedEventArgs
对象可定义 Element、Mode
、Position
、ScrollX
、ScrollY
和ShouldAnimate
属性。
使用数据填充 ListView
可通过将其 ItemsSource
属性设置为实现 IEnumerable
的任何集合来为 ListView 填充数据。
重要
如果在基础集合中添加、删除或更改项时需要使用 ListView 来进行刷新,则基础集合应是发送属性更改通知的 IEnumerable
集合,例如 ObservableCollection
。
可通过使用数据绑定将其 ItemsSource
属性绑定到 IEnumerable
集合来为 ListView 填充数据。 在 XAML 中,可通过 Binding
标记扩展达成此目的:
<ListView ItemsSource="{Binding Monkeys}" />
等效 C# 代码如下:
ListView listView = new ListView();
listView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");
在此示例中,ItemsSource
属性数据绑定到连接 viewmodel 的 Monkeys
属性。
注意
可以启用已编译的绑定,以提高 .NET MAUI 应用程序中的数据绑定性能。 有关详细信息,请参阅已编译的绑定。
有关数据绑定的详细信息,请参阅数据绑定。
定义项外观
通过将 ItemTemplate
属性设置为 DataTemplate 来定义的 ListView 中每个项的外观:
<ListView ItemsSource="{Binding Monkeys}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
DataTemplate 中指定的元素定义列表中每个项的外观,且 DataTemplate 的子元素必须是 Cell 对象。 在此示例中,DataTemplate 的内部布局由 Grid 管理。 Grid 包含一个 Image 对象和两个 Label 对象,它们都绑定到 Monkey
类的属性:
public class Monkey
{
public string Name { get; set; }
public string Location { get; set; }
public string Details { get; set; }
public string ImageUrl { get; set; }
}
以下屏幕截图显示对列表中每个项进行模板化的结果:
有关数据模板的详细信息,请参阅数据模板。
单元
ListView 中每个项的外观由 DataTemplate 定义,且 DataTemplate 必须引用 Cell 类来显示项。 每个单元格表示 ListView 中的数据项。 .NET MAUI 包含以下内置单元格:
- TextCell,用于在单独的行上显示主要文本和辅助文本。
- ImageCell,用于在单独的行上显示带有主要文本和辅助文本的图像。
- SwitchCell,用于显示文本以及可打开或关闭的开关。
- EntryCell,用于显示可编辑的标签和文本。
- ViewCell,是一个自定义单元格,其外观由 View 定义。 如果要完全定义 ListView 中每个项的外观,则应使用此单元格类型。
通常,SwitchCell 和 EntryCell 仅用于 TableView,而不会用于 ListView。 有关 SwitchCell 和 EntryCell 的详细信息,请参阅 TableView。
文本单元格
TextCell 在单独的行上显示主要文本和辅助文本。 TextCell 定义以下属性:
Text
,类型为string
,定义要显示的主要文本。TextColor
,类型为 Color,表示主要文本的颜色。Detail
,类型为string
,定义要显示的辅助文本。DetailColor
,类型为 Color,指示辅助文本的颜色。Command
,类型为 ICommand,定义在点击单元格时执行的命令。CommandParameter
,类型为object
,表示传递给命令的参数。
这些属性由 BindableProperty 对象提供支持;也就是说,它们可以作为数据绑定的目标,并能进行样式设置。
下面的示例展示了如何使用 TextCell 更改 ListView 中各项的外观:
<ListView ItemsSource="{Binding Food}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}"
Detail="{Binding Description}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
以下屏幕截图展示了生成的单元格外观:
图像单元格
ImageCell 在单独的行上显示带有主要文本和辅助文本的图像。 ImageCell 从 TextCell 继承属性,并定义 ImageSource 类型的 ImageSource 属性,指定要显示在单元格中的图像。 此属性由 BindableProperty 对象提供支持,这意味着可以将它用作数据绑定的目标,并能对它进行样式设置。
下面的示例展示了如何使用 ImageCell 定义 ListView 中各项的外观:
<ListView ItemsSource="{Binding Food}">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell ImageSource="{Binding Image}"
Text="{Binding Name}"
Detail="{Binding Description}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
以下屏幕截图展示了生成的单元格外观:
查看单元格
ViewCell 是自定义单元格,其外观由 View 定义。 ViewCell 定义类型为 View 的 View 属性,该属性可定义表示单元格内容的视图。 此属性由 BindableProperty 对象提供支持,这意味着可以将它用作数据绑定的目标,并能对它进行样式设置。
以下示例展示了如何使用 ViewCell 定义 ListView 中项的外观:
<ListView ItemsSource="{Binding Monkeys}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
在 ViewCell 内部,布局可由任何 .NET MAUI 布局管理。 在此示例中,布局由 Grid 管理。 Grid 包含一个 Image 对象和两个 Label 对象,它们都绑定到 Monkey
类的属性。
以下屏幕截图显示对列表中每个项进行模板化的结果:
在运行时选择项外观
通过将 ItemTemplate
属性设置为 DataTemplateSelector 对象,可以在运行时根据项值选择 ListView 中每个项的外观:
<ContentPage ...
xmlns:templates="clr-namespace:ListViewDemos.Templates">
<ContentPage.Resources>
<DataTemplate x:Key="AmericanMonkeyTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="OtherMonkeyTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<templates:MonkeyDataTemplateSelector x:Key="MonkeySelector"
AmericanMonkey="{StaticResource AmericanMonkeyTemplate}"
OtherMonkey="{StaticResource OtherMonkeyTemplate}" />
</ContentPage.Resources>
<StackLayout Margin="20">
<ListView ItemsSource="{Binding Monkeys}"
ItemTemplate="{StaticResource MonkeySelector}" />
</StackLayout>
</ContentPage>
ItemTemplate
属性设置为 MonkeyDataTemplateSelector
对象。 下面的示例展示了 MonkeyDataTemplateSelector
类:
public class MonkeyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate AmericanMonkey { get; set; }
public DataTemplate OtherMonkey { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return ((Monkey)item).Location.Contains("America") ? AmericanMonkey : OtherMonkey;
}
}
MonkeyDataTemplateSelector
类定义 AmericanMonkey
和 OtherMonkey
DataTemplate 属性,它们被设置为不同的数据模板。 当猴子名称包含“美国”时,OnSelectTemplate
重写函数会返回 AmericanMonkey
模板,该模板以青色显示猴子名称和位置。 当猴子名称不包含“美国”时,OnSelectTemplate
重写函数会返回 OtherMonkey
模板,该模板以银色显示猴子名称和位置:
有关数据模板选择器的详细信息,请参阅创建 DataTemplateSelector。
响应项选择
默认情况下,ListView 选择处于启用状态。 但是,可以通过设置 SelectionMode
属性来更改此行为。 ListViewSelectionMode
枚举定义下列成员:
None
– 指示无法选择项。Single
– 指示可以选择单个项,选定项会突出显示。 这是默认值。
ListView 定义当 SelectedItem
属性发生更改时引发的 ItemSelected
事件,变化的原因包括用户从列表中选择项以及应用设置该属性。 此事件附带的 SelectedItemChangedEventArgs
对象包含 SelectedItem
和 SelectedItemIndex
属性。
当 SelectionMode
属性被设置为 Single
时,可以选择 ListView 中的单个项。 选中某个项时,SelectedItem
属性会被设置为所选项的值。 当此属性发生更改时,也会引发 ItemSelected
事件。
以下示例展示了可以响应单项选择的 ListView:
<ListView ItemsSource="{Binding Monkeys}"
ItemSelected="OnItemSelected">
...
</ListView>
在此示例中,当 ItemSelected
事件被触发时,将执行 OnItemSelected
事件处理程序,且事件处理程序会检索所选项:
void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
Monkey item = args.SelectedItem as Monkey;
}
以下屏幕截图展示了在 ListView 中选择的单个项:
清除选择
可以通过将 SelectedItem
属性或其绑定的对象设置为 null
来清除此属性。
禁用选择
默认情况下,ListView 选择处于启用状态。 但是,可以通过将 SelectionMode
属性设置为 None
来禁用它:
<ListView ...
SelectionMode="None" />
如果 SelectionMode
属性被设置为 None
,则无法选择 ListView 中的项,SelectedItem
属性将保持为 null
,并且不会触发 ItemSelected
事件。
缓存数据
ListView 是一种功能强大的数据显示视图,但也有一些局限性。 使用自定义单元格时,滚动性能可能会受到影响,尤其是当单元格包含深度嵌套的视图层次结构或使用某些需要复杂度量的布局时。 幸运的是,可以使用一些技巧来避免出现性能欠佳的情况:
ListView 通常用于显示屏幕上容纳不下的大量数据。 例如,音乐应用的歌曲库可能有数千个条目。 为每个条目创建一个项会浪费宝贵的内存,并导致性能欠佳。 如果不断创建和销毁行,应用就必须不断实例化和清理对象,这也会导致性能欠佳。
为了节省内存,每个平台的本机 ListView 等效项都具有可供重复使用行的内置功能。 只有屏幕上可见的单元格才会被载入内存,而内容将加载到现有单元格中。 这种模式可以防止应用实例化数千个对象,从而节省时间和内存。
.NET MAUI 通过 ListViewCachingStrategy
枚举允许重复使用 ListView 单元,该枚举定义了以下成员:
RetainElement
,指定 ListView 将为列表中的每个项生成一个单元格。RecycleElement
,指定 ListView 将尝试通过回收列表单元格来尽量减少其内存占用和降低执行速度。RecycleElementAndDataTemplate
,类似RecycleElement
,同时还确保 DataTemplate 对象在 ListView 使用 DataTemplateSelector 时按列表中的项类型进行缓存。
保留元素
RetainElement
缓存策略指定 ListView 将为列表中的每个项生成一个单元格,并且是默认的 ListView 行为。 应在以下情况下使用它:
- 每个单元格都有大量绑定 (20-30+)。
- 单元格模板经常更改。
- 测试显示,
RecycleElement
缓存策略会导致执行速度降低。
使用自定义单元格时,必须认识到 RetainElement
缓存策略的后果。 每次创建单元格时都需要运行单元格初始化代码,该代码可能会每秒运行多次。 在这种情况下,页面上这种原本正常的布局技术(如使用多个嵌套 StackLayout 对象)若在用户滚动时实时设置和销毁,就会成为性能瓶颈。
回收元素
RecycleElement
缓存策略指定 ListView 将尝试通过回收列表单元格来尽量减少其内存占用和降低执行速度。 这种模式并不总能提高性能,应执行测试以确定是否有任何改进。 但是,这是首选方案,并应在以下情况下使用:
- 每个单元格都有少量到中等数量的绑定。
- 每个单元格的
BindingContext
定义所有单元格数据。 - 每个单元格大体相似,单元格模板不变。
在虚拟化期间,将会更新单元格的绑定上下文,因此,如果应用使用这种模式,必须确保妥善处理绑定上下文更新。 有关单元格的所有数据都必须来自绑定上下文,否则可能会出现一致性错误。 通过使用数据绑定来显示单元格数据,可以避免此问题。 另外,单元格数据应在 OnBindingContextChanged
替代中设置,而不是在自定义单元格的构造函数中设置,如以下示例所示:
public class CustomCell : ViewCell
{
Image image = null;
public CustomCell()
{
image = new Image();
View = image;
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
var item = BindingContext as ImageItem;
if (item != null)
{
image.Source = item.ImageUrl;
}
}
}
使用 DataTemplateSelector 回收元素
当 ListView 使用 DataTemplateSelector 选择 DataTemplate 时,RecycleElement
缓存策略不会缓存 DataTemplate 对象。 而是会为列表中的每个数据项选择一个 DataTemplate。
注意
RecycleElement
缓存策略要求,当系统要求 DataTemplateSelector 选择 DataTemplate 时,每个 DataTemplate 必须返回相同的 ViewCell 类型。 例如,假设有一个带有可返回 MyDataTemplateA
(其中 MyDataTemplateA
返回一个类型为 MyViewCellA
的 ViewCell)或 MyDataTemplateB
(其中 MyDataTemplateB
返回一个类型为 MyViewCellB
的 ViewCell)的 DataTemplateSelector 的 ListView,返回 MyDataTemplateA
时必须返回 MyViewCellA
,否则将引发异常。
使用 DataTemplates 回收元素
RecycleElementAndDataTemplate
缓存策略建立在 RecycleElement
缓存策略基础上,还额外确保了当 ListView 使用 DataTemplateSelector 选择 DataTemplate 时,DataTemplate 对象将按列表中的项类型进行缓存。 因此,会针对每个项类型选择一次 DataTemplate 对象,而不是针对每个项实例选择一次。
注意
RecycleElementAndDataTemplate
缓存策略要求由 DataTemplateSelector 返回的 DataTemplate 对象必须使用采用 Type
的 DataTemplate 构造函数。
设置缓存策略
在 XAML 中,可以通过设置 CachingStrategy
特性来定义 ListView 缓存策略:
<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
...
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
在 C# 中,缓存策略是通过构造函数重载设置的:
ListView listView = new ListView(ListViewCachingStrategy.RecycleElement);
在子类化 ListView 中设置缓存策略
在子类化 ListView 上从 XAML 设置 CachingStrategy
属性不会生成所需的行为,因为 ListView 上没有 CachingStrategy
属性。 此问题的解决方案是,在接受 ListViewCachingStrategy
参数并将其传递给基类的子类化 ListView 上指定构造函数:
public class CustomListView : ListView
{
public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
{
}
...
}
然后,可以使用 x:Arguments
属性从 XAML 指定 ListViewCachingStrategy
枚举值:
<local:CustomListView>
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</local:CustomListView>
页眉和页脚
ListView 可以显示随列表中的项一起滚动的页眉和页脚。 页眉和页脚可以是字符串、视图或 DataTemplate 对象。
ListView 定义用于指定页眉和页脚的以下属性:
Header
,类型为object
,指定将在列表开头显示的字符串、绑定或视图。HeaderTemplate
,类型为 DataTemplate,指定用于设置Header
格式的 DataTemplate。Footer
,类型为object
,指定将在列表末尾显示的字符串、绑定或视图。FooterTemplate
,类型为 DataTemplate,指定用于设置Footer
格式的 DataTemplate。
所有这些属性都由 BindableProperty 对象提供支持,这意味着这些属性可以作为数据绑定的目标。
在页眉和页脚中显示字符串
但是,可以将 Header
和 Footer
属性设置为 string
值,如以下示例中所示:
<ListView ItemsSource="{Binding Monkeys}"
Header="Monkeys"
Footer="2022">
...
</ListView>
以下屏幕截图显示生成的页眉:
在页眉和页脚中显示视图
可以分别将 Header
和 Footer
属性设置为视图。 这可以是单个视图,也可以是包含多个子视图的视图。 以下示例展示了 Header
和 Footer
属性均被设置为 StackLayout 对象,该对象包含一个 Label 对象:
<ListView ItemsSource="{Binding Monkeys}">
<ListView.Header>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Monkeys"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</ListView.Header>
<ListView.Footer>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Friends of Monkey"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</ListView.Footer>
...
</ListView>
以下屏幕截图显示生成的页眉:
显示模板化的页眉和页脚
可以将 HeaderTemplate
和 FooterTemplate
属性设置为 DataTemplate 对象,该对象用于设置页眉和页脚格式。 在此方案中,对于要应用的模板,Header
和 Footer
属性必须绑定到当前源,如以下示例所示:
<ListView ItemsSource="{Binding Monkeys}"
Header="{Binding .}"
Footer="{Binding .}">
<ListView.HeaderTemplate>
<DataTemplate>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Monkeys"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.FooterTemplate>
<DataTemplate>
<StackLayout BackgroundColor="LightGray">
<Label Margin="10,0,0,0"
Text="Friends of Monkey"
FontSize="12"
FontAttributes="Bold" />
</StackLayout>
</DataTemplate>
</ListView.FooterTemplate>
...
</ListView>
控制项分隔符
默认情况下,iOS 和 Android 上的 ListView 项之间会显示分隔符。 可以通过将类型为 SeparatorVisibility
的 SeparatorVisibility
属性设置为 None
来更改此行为:
<ListView ...
SeparatorVisibility="None" />
此外,启用分隔符后,还可以使用 SeparatorColor
属性设置其颜色:
<ListView ...
SeparatorColor="Blue" />
调整项的大小
默认情况下,ListView 中所有项的高度都相同,这些高度派生自定义每个项外观的 DataTemplate 的内容。 但是,可以使用 HasUnevenRows
和 RowHeight
属性更改此行为。 默认情况下,HasUnevenRows
属性为 false
。
如果 HasUnevenRows
为 false
,则 RowHeight
属性可以设置为表示 ListView 中每个项高度的 int
。 将 HasUnevenRows
设置为 true
时,ListView 中每个项的高度可能都不同。 每个项的高度将派生自项的 DataTemplate 的内容,因此将对每个项调整大小以与其内容保持一致。
如果 HasUnevenRows
属性为 true
,则可以在运行时通过更改 DataTemplate 中元素的布局相关属性,以编程方式调整单个 ListView 项的大小。 以下示例在点击 Image 对象时更改该对象的高度:
void OnImageTapped(object sender, EventArgs args)
{
Image image = sender as Image;
ViewCell viewCell = image.Parent.Parent as ViewCell;
if (image.HeightRequest < 250)
{
image.HeightRequest = image.Height + 100;
viewCell.ForceUpdateSize();
}
}
在此示例中,在点击 Image 对象时将执行 OnImageTapped
事件处理程序。 事件处理程序会更新 Image 的高度,而 Cell.ForceUpdateSize
方法会更新单元格的大小,即使该单元格当前不可见。
警告
过度使用动态调整项大小可能会导致 ListView 性能下降。
从右到左布局
ListView 可以通过将其 FlowDirection
属性设置为 RightToLeft
,按从右到左的流动方向布局其内容。 但是,最好在页面或根布局上设置 FlowDirection
属性,这样会使页面或根布局中的所有元素响应流动方向:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ListViewDemos.RightToLeftListPage"
Title="Right to left list"
FlowDirection="RightToLeft">
<StackLayout Margin="20">
<ListView ItemsSource="{Binding Monkeys}">
...
</ListView>
</StackLayout>
</ContentPage>
具有父元素的元素默认 FlowDirection
为 MatchParent
。 因此,ListView 会从 StackLayout 继承 FlowDirection
属性值,而中间的项又会从 ContentPage 继承 FlowDirection
属性值。
显示分组数据
在持续滚动的列表中显示时,大型数据集通常会变得笨拙。 在此方案中,将数据组织成组可以简化数据导航,从而改善用户体验。
必须先对数据进行分组,然后才能显示数据。 这可以通过创建组列表来实现,其中每个组都是项列表。 组列表应为 IEnumerable<T>
集合,其中 T
定义两条数据:
- 组名称。
- 定义组内项的
IEnumerable
集合。
因此,分组数据的过程为:
- 创建对单个项建模的类型。
- 创建对单个项组建模的类型。
- 创建
IEnumerable<T>
集合,其中T
为对单个项组建模的类型。 此集合是组的集合,用于存储分组数据。 - 将数据添加到
IEnumerable<T>
集合。
示例
对数据分组时,第一步是创建对单个项进行建模的类型。 下面的示例展示了 Animal
类:
public class Animal
{
public string Name { get; set; }
public string Location { get; set; }
public string Details { get; set; }
public string ImageUrl { get; set; }
}
Animal
类对单个项建模。 然后,可以创建对项组建模的类型。 下面的示例展示了 AnimalGroup
类:
public class AnimalGroup : List<Animal>
{
public string Name { get; private set; }
public AnimalGroup(string name, List<Animal> animals) : base(animals)
{
Name = name;
}
}
AnimalGroup
类继承自 List<T>
类,并添加表示组名的 Name
属性。
然后,可以创建 IEnumerable<T>
组集合:
public List<AnimalGroup> Animals { get; private set; } = new List<AnimalGroup>();
此代码定义名为 Animals
的集合,其中集合中的每个项都是一个 AnimalGroup
对象。 每个 AnimalGroup
对象都包含名称和定义组中 Animal
对象的 List<Animal>
集合。
然后,可将分组数据添加到 Animals
集合中:
Animals.Add(new AnimalGroup("Bears", new List<Animal>
{
new Animal
{
Name = "American Black Bear",
Location = "North America",
Details = "Details about the bear go here.",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/0/08/01_Schwarzbär.jpg"
},
new Animal
{
Name = "Asian Black Bear",
Location = "Asia",
Details = "Details about the bear go here.",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b7/Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG/180px-Ursus_thibetanus_3_%28Wroclaw_zoo%29.JPG"
},
// ...
}));
Animals.Add(new AnimalGroup("Monkeys", new List<Animal>
{
new Animal
{
Name = "Baboon",
Location = "Africa & Asia",
Details = "Details about the monkey go here.",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_anubis_%28Serengeti%2C_2009%29.jpg/200px-Papio_anubis_%28Serengeti%2C_2009%29.jpg"
},
new Animal
{
Name = "Capuchin Monkey",
Location = "Central & South America",
Details = "Details about the monkey go here.",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Capuchin_Costa_Rica.jpg/200px-Capuchin_Costa_Rica.jpg"
},
new Animal
{
Name = "Blue Monkey",
Location = "Central and East Africa",
Details = "Details about the monkey go here.",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/BlueMonkey.jpg/220px-BlueMonkey.jpg"
},
// ...
}));
此代码在 Animals
集合中创建两个组。 第一个 AnimalGroup
名为 Bears
,包含熊详细信息的 List<Animal>
集合。 第二个 AnimalGroup
名为 Monkeys
,包含猴子详细信息的 List<Animal>
集合。
如果已通过将 IsGroupingEnabled
属性设置为 true
对数据进行了正确分组,则 ListView 会显示分组数据:
<ListView ItemsSource="{Binding Animals}"
IsGroupingEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold" />
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Location}"
FontAttributes="Italic"
VerticalOptions="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
等效 C# 代码如下:
ListView listView = new ListView
{
IsGroupingEnabled = true
};
listView.SetBinding(ItemsView.ItemsSourceProperty, "Animals");
// ...
通过将 ItemTemplate
属性设置为 DataTemplate 来定义 ListView 中每个项的外观。 有关详细信息,请参阅定义项外观。
以下屏幕截图显示展示分组数据的 ListView:
注意
默认情况下, ListView 将在组标头中显示组名称。 可以通过自定义组标头来更改此行为。
自定义组标头
可以通过将 ListView.GroupHeaderTemplate
属性设置为 DataTemplate 来自定义每个组标头的外观:
<ListView ItemsSource="{Binding Animals}"
IsGroupingEnabled="True">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}"
BackgroundColor="LightGray"
FontSize="18"
FontAttributes="Bold" />
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
...
</ListView>
在此示例中,每个组标头都设置为显示组名的 Label,并且设置了其他外观属性。 以下屏幕截图显示了自定义组标头:
重要
GroupHeaderTemplate
属性与 GroupDisplayBinding
属性互斥。 因此,不应设置这两个属性。
没有模板的组
ListView 可在不将 ItemTemplate
属性设置为 DataTemplate 的情况下正确显示分组数据:
<ListView ItemsSource="{Binding Animals}"
IsGroupingEnabled="true" />
在此方案中,可以通过替代单个项建模类型和单个项组建模类型中的 ToString
方法来显示有意义的数据。
控制滚动
ListView 定义两种 ScrollTo
方法,用于将项滚动到视图中。 其中一种重载会将指定项滚动到视图中,而另一种重载会将指定组中的指定项滚动到视图中。 这两种重载都有附加参数,这些参数允许指定滚动完成后项的确切位置,以及是否对滚动进行动画处理。
ListView 定义了调用 ScrollTo
方法之一时触发的 ScrollToRequested
事件。 ScrollToRequestedEventArgs
事件随附的 ScrollToRequested
对象包含许多属性,包括 ShouldAnimate
、Element、Mode
和 Position
。 其中一些属性是根据 ScrollTo
方法调用中指定的参数设置的。
此外, ListView 还定义触发以指示发生了滚动的 Scrolled
事件。 Scrolled
事件随附的 ScrolledEventArgs
对象包含 ScrollX
和 ScrollY
属性。
检测滚动
ListView 定义 Scrolled
事件,触发该事件表示发生了滚动。 ItemsViewScrolledEventArgs
类表示 Scrolled
事件随附的对象,它定义下列属性:
ScrollX
,类型为double
,表示滚动的 X 位置ScrollY
,类型为double
,表示滚动的 Y 位置。
以下 XAML 示例显示为 Scrolled
事件设置事件处理程序的 ListView:
<ListView Scrolled="OnListViewScrolled">
...
</ListView>
等效 C# 代码如下:
ListView listView = new ListView();
listView.Scrolled += OnListViewScrolled;
在此代码示例中,触发 Scrolled
事件时,将执行 OnListViewScrolled
事件处理程序:
void OnListViewScrolled(object sender, ScrolledEventArgs e)
{
// Custom logic
}
重要
针对用户启动的滚动和编程滚动触发 Scrolled
事件。
将项滚动到视图中
ScrollTo
方法将指定项滚动到视图中。 给定名为 listView
的 ListView 对象,以下示例演示如何将“长鼻猴”项滚动到视图中:
MonkeysViewModel viewModel = BindingContext as MonkeysViewModel;
Monkey monkey = viewModel.Monkeys.FirstOrDefault(m => m.Name == "Proboscis Monkey");
listView.ScrollTo(monkey, ScrollToPosition.MakeVisible, true);
或者,可以通过指定项和组将分组数据中的项滚动到视图中。 以下示例演示如何将“猴子”组中的“长鼻猴”项滚动到视图中:
GroupedAnimalsViewModel viewModel = BindingContext as GroupedAnimalsViewModel;
AnimalGroup group = viewModel.Animals.FirstOrDefault(a => a.Name == "Monkeys");
Animal monkey = group.FirstOrDefault(m => m.Name == "Proboscis Monkey");
listView.ScrollTo(monkey, group, ScrollToPosition.MakeVisible, true);
注意
调用 ScrollTo
方法时,将触发 ScrollToRequested
事件。
禁用滚动动画
将项滚动到视图中时,将显示滚动动画。 但是,可以通过将 ScrollTo
方法的 animated
参数设置为 false
来禁用此动画:
listView.ScrollTo(monkey, position: ScrollToPosition.MakeVisible, animate: false);
控制滚动位置
将项滚动到视图中时,可以使用 ScrollTo
方法的 position
参数指定项在滚动完成后的确切位置。 此参数接受 ScrollToPosition
枚举成员。
MakeVisible
ScrollToPosition.MakeVisible
成员指示应滚动该项,直到它在视图中可见:
listView.ScrollTo(monkey, position: ScrollToPosition.MakeVisible, animate: true);
开始
ScrollToPosition.Start
成员指示应将项滚动到视图的开头:
listView.ScrollTo(monkey, position: ScrollToPosition.Start, animate: true);
中心
ScrollToPosition.Center
成员指示应将项滚动到视图的中心:
listView.ScrollTo(monkey, position: ScrollToPosition.Center, animate: true);
End
ScrollToPosition.End
成员指示应将项滚动到视图的末尾:
listView.ScrollTo(monkey, position: ScrollToPosition.End, animate: true);
滚动条可见性
ListView 定义 HorizontalScrollBarVisibility
和 VerticalScrollBarVisibility
属性,这些属性由可绑定属性提供支持。 这些属性会获取或设置一个 ScrollBarVisibility
枚举值,指示水平滚动条或垂直滚动条何时可见。 ScrollBarVisibility
枚举定义下列成员:
Default
指示平台的默认滚动条行为,并且是HorizontalScrollBarVisibility
和VerticalScrollBarVisibility
属性的默认值。Always
指示滚动条将可见,即使内容适合视图也是如此。Never
指示滚动条将不可见,即使内容不适合视图也是如此。
添加上下文菜单
ListView 支持上下文菜单项,这些项被定义为 MenuItem 对象,并添加到每个项的 DataTemplate 中的 ViewCell.ContextActions
集合:
<ListView x:Name="listView"
ItemsSource="{Binding Monkeys}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Favorite"
Command="{Binding Source={x:Reference listView}, Path=BindingContext.FavoriteCommand}"
CommandParameter="{Binding}" />
<MenuItem Text="Delete"
Command="{Binding Source={x:Reference listView}, Path=BindingContext.DeleteCommand}"
CommandParameter="{Binding}" />
</ViewCell.ContextActions>
...
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
右键单击 ListView 中的项时,将显示 MenuItem 对象:
有关菜单项的详细信息,请参阅显示菜单项。
下拉刷新
ListView 支持“下拉以刷新”功能,通过向下拉动项目列表,可刷新显示数据。
若要启用“下拉以刷新”,请将 IsPullToRefreshEnabled
属性设置为 true
。 触发刷新时,ListView 会引发 Refreshing
事件,并将 IsRefreshing
属性设置为 true
。 然后,刷新 ListView 内容所需的代码应由 Refreshing
事件的处理程序来执行,或由 RefreshCommand
执行的 ICommand 实现来执行。 刷新 ListView 后,应将 IsRefreshing
属性设置为 false
,或在 ListView 上调用 EndRefresh
方法,以指示刷新已完成。
以下示例演示使用“下拉以刷新”的 ListView:
<ListView ItemsSource="{Binding Animals}"
IsPullToRefreshEnabled="true"
RefreshCommand="{Binding RefreshCommand}"
IsRefreshing="{Binding IsRefreshing}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
...
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
在此示例中,当用户启动刷新时,系统将执行 RefreshCommand
属性定义的 ICommand,从而刷新显示项。 刷新时会显示刷新可视化效果,其中包含动画形式的圆形进度条。 IsRefreshing
属性的值指示刷新操作的当前状态。 触发刷新时,此属性将自动转换为 true
。 刷新完成后,应将属性重置为 false
。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈