<ListView ...
SelectionMode="None" />
当 属性 SelectionMode 设置为 None时,无法选择 中的 ListView 项,该 SelectedItem 属性将保留 null,并且 ItemSelected 不会触发事件。
ListView 是用于显示数据的强大视图,但它有一些限制。 使用自定义单元格时,滚动性能可能会受到影响,尤其是当它们包含深层嵌套的视图层次结构或使用某些需要复杂度量的布局时。 幸运的是,可以使用一些技术来避免性能不佳。
ListView通常用于显示比屏幕上适合的数据要多得多。 例如,音乐应用可能有一个包含数千个条目的歌曲库。 为每个条目创建一个项会浪费宝贵的内存,并且性能不佳。 不断创建和销毁行需要应用不断实例化和清理对象,这也会导致性能不佳。
为了节省内存,每个平台的本机 ListView 等效项都具有用于重用行的内置功能。 只有屏幕上可见的单元格才会加载到内存中,内容将加载到现有单元格中。 此模式可防止应用实例化数千个对象,从而节省时间和内存。
.NET MAUI 允许 ListView 通过 ListViewCachingStrategy 枚举重用单元格,该枚举定义了以下成员:
RetainElement,指定 ListView 将为列表中的每个项生成一个单元格。
RecycleElement,指定 ListView 将尝试通过回收列表单元格来最小化其内存占用和执行速度。
RecycleElementAndDataTemplate,同时RecycleElement确保 使用 DataTemplateSelector时ListViewDataTemplate,对象按列表中的项类型进行缓存。
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 类型。 例如,如果给定一个 ListViewDataTemplateSelector ,其可以返回MyDataTemplateA (,其中MyDataTemplateA返回ViewCell) 类型的 MyViewCellA ,或者MyDataTemplateB (返回MyDataTemplateBViewCell) 类型的 MyViewCellB ,当返回 时MyDataTemplateA,必须返回MyViewCellA它,否则将引发异常。
使用 DataTemplates 回收元素
缓存RecycleElementAndDataTemplate策略基于RecycleElement缓存策略,另外确保当 ListView 使用 DataTemplateSelector 选择 时DataTemplateDataTemplate,对象按列表中的项类型进行缓存。 因此, DataTemplate 每个项类型选择一次对象,而不是每个项实例选择一次对象。
RecycleElementAndDataTemplate缓存策略要求 DataTemplate 由 DataTemplateSelector 返回的对象必须使用DataTemplate采用 的Type构造函数。
设置缓存策略
ListView可以通过在 XAML 中通过设置 属性来CachingStrategy定义缓存策略:
<ListView CachingStrategy="RecycleElement">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
在 C# 中,缓存策略是通过构造函数重载设置的:
ListView listView = new ListView(ListViewCachingStrategy.RecycleElement);
在子类化 ListView 中设置缓存策略
CachingStrategy在子类上ListView从 XAML 设置 属性不会生成所需的行为,因为 上ListView没有 CachingStrategy 属性。 此问题的解决方案是在子ListViewCachingStrategy类ListView上指定接受参数并将其传递给基类的构造函数:
public class CustomListView : ListView
public CustomListView (ListViewCachingStrategy strategy) : base (strategy)
然后, ListViewCachingStrategy 可以使用 属性从 XAML x:Arguments 指定枚举值:
<local:CustomListView>
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
</local:CustomListView>
ListView 可以显示随列表中的项一起滚动的页眉和页脚。 页眉和页脚可以是字符串、视图或 DataTemplate 对象。
ListView 定义用于指定页眉和页脚的以下属性:
Header类型 object为 ,指定将在列表开头显示的字符串、绑定或视图。
HeaderTemplate,属于 类型 DataTemplate,指定要 DataTemplate 用于设置 格式的 Header。
Footer类型 object为 ,指定将在列表末尾显示的字符串、绑定或视图。
FooterTemplate,属于 类型 DataTemplate,指定要 DataTemplate 用于设置 格式的 Footer。
这些属性由 BindableProperty 对象提供支持,这意味着这些属性可以是数据绑定的目标。
HeaderFooter和 属性可以设置为string值,如以下示例所示:
<ListView ItemsSource="{Binding Monkeys}"
Header="Monkeys"
Footer="2022">
</ListView>
以下屏幕截图显示了生成的标头:
和 HeaderFooter 属性都可以分别设置为视图。 这可以是单个视图,也可以是包含多个子视图的视图。 以下示例演示Header每个设置为StackLayout包含 Label 对象的 和 Footer 属性:
<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 定义每个项外观的 的内容。 但是,可以使用 和 RowHeight 属性更改HasUnevenRows此行为。 默认情况下, HasUnevenRows 属性为 false。
可以将 RowHeight 属性设置为表示 int 中 ListView每个项的高度的 ,前提是 HasUnevenRows 为 false。 当 设置为 true时HasUnevenRows,中的每个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();
在此示例中, OnImageTapped 执行事件处理程序以响应 Image 正在点击的对象。 事件处理程序更新 的高度 Image ,方法 Cell.ForceUpdateSize 更新单元格的大小,即使当前不可见。
过度使用动态项大小可能会导致 ListView 性能下降。
从右到左布局
ListView可以通过将其 属性设置为 RightToLeft来按从右到左的流方向布局其FlowDirection内容。 但是,理想情况下, 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 对象都包含一个名称和一个 List<Animal> 定义 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> 猴子详细信息的集合。
ListView 将显示分组数据,前提是数据已正确分组,方法是将 IsGroupingEnabled 属性设置为 true:
<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");
// ...
中每个项 ListView 的外观是通过将其 ItemTemplate 属性设置为 DataTemplate来定义的。 有关详细信息,请参阅 定义项外观。
以下屏幕截图显示了显示 ListView 分组数据:
默认情况下, ListView 将在组标头中显示组名称。 可以通过自定义组标头来更改此行为。
可以通过将 属性设置为 ListView.GroupHeaderTemplateDataTemplate来自定义每个组标头的外观:
<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 以指示发生滚动的事件。 ScrolledEventArgs事件附带的对象Scrolled具有 ScrollX 和 ScrollY 属性。
ListView 定义触发 Scrolled 以指示发生滚动的事件。 ItemsViewScrolledEventArgs类表示事件附带的对象Scrolled,定义了以下属性:
ScrollX,类型 double为 ,表示滚动的 X 位置
ScrollY,类型 double为 ,表示滚动的 Y 位置。
以下 XAML 示例演示一个 ListView ,它为 Scrolled 事件设置事件处理程序:
<ListView Scrolled="OnListViewScrolled">
</ListView>
等效 C# 代码如下:
ListView listView = new ListView();
listView.Scrolled += OnListViewScrolled;
在此代码示例中 OnListViewScrolled ,事件处理程序在触发事件时 Scrolled 执行:
void OnListViewScrolled(object sender, ScrolledEventArgs e)
// Custom logic
对于 Scrolled 用户启动的滚动和编程滚动,将触发 事件。
方法 ScrollTo 将指定的项滚动到视图中。 给定一 ListView 个名为 listView的对象,以下示例演示如何将 Proboscis Monkey 项滚动到视图中:
MonkeysViewModel viewModel = BindingContext as MonkeysViewModel;
Monkey monkey = viewModel.Monkeys.FirstOrDefault(m => m.Name == "Proboscis Monkey");
listView.ScrollTo(monkey, ScrollToPosition.MakeVisible, true);
或者,可以通过指定项和组,将分组数据中的项滚动到视图中。 以下示例演示如何将 Monkeys 组中的 Proboscis Monkey 项滚动到视图中:
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);
调用 ScrollToRequested 方法时会 ScrollTo 触发 事件。
将项滚动到视图中时,将显示滚动动画。 但是,可以通过将 方法的参数ScrollTo设置为 animatedfalse来禁用此动画:
listView.ScrollTo(monkey, position: ScrollToPosition.MakeVisible, animate: false);
将项滚动到视图中时,可以使用 方法的 参数指定 position 滚动完成后项的 ScrollTo 确切位置。 此参数接受 ScrollToPosition 枚举成员。
MakeVisible
成员 ScrollToPosition.MakeVisible 指示应滚动该项,直到它在视图中可见:
listView.ScrollTo(monkey, position: ScrollToPosition.MakeVisible, animate: true);
成员 ScrollToPosition.Start 指示应将项滚动到视图的开头:
listView.ScrollTo(monkey, position: ScrollToPosition.Start, animate: true);
Center
成员 ScrollToPosition.Center 指示应将项滚动到视图的中心:
listView.ScrollTo(monkey, position: ScrollToPosition.Center, animate: true);
成员 ScrollToPosition.End 指示应将项滚动到视图的末尾:
listView.ScrollTo(monkey, position: ScrollToPosition.End, animate: true);
ListViewHorizontalScrollBarVisibility定义和VerticalScrollBarVisibility属性,它们由可绑定属性提供支持。 这些属性获取或设置一个 ScrollBarVisibility 枚举值,该值表示水平滚动条或垂直滚动条何时可见。 ScrollBarVisibility 枚举定义下列成员:
Default指示平台的默认滚动条行为,是 和 VerticalScrollBarVisibility 属性的HorizontalScrollBarVisibility默认值。
Always 指示滚动条将可见,即使内容适合视图。
Never 指示滚动条将不可见,即使内容不适合视图。
ListView 支持上下文菜单项,这些项定义为 MenuItem 添加到每个项的 ViewCell.ContextActions 集合中的 DataTemplate 对象:
<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>
MenuItem右键单击 中的项时,ListView会显示 对象:
ListView 支持拉取刷新功能,该功能允许通过下拉项列表来刷新显示的数据。
若要启用要刷新的拉取,请将 IsPullToRefreshEnabled 属性设置为 true。 触发刷新时, ListView 引发 Refreshing 事件,并且 IsRefreshing 属性将设置为 true。 然后,刷新 的内容 ListView 所需的代码应由 事件的处理程序 Refreshing 执行,或由 ICommand 执行的实现 RefreshCommand 执行。 ListView刷新 后,IsRefreshing属性应设置为 false,或者EndRefresh应在 上ListView调用 方法,以指示刷新已完成。
以下示例演示 ListView 了使用拉取刷新的 :
<ListView ItemsSource="{Binding Animals}"
IsPullToRefreshEnabled="true"
RefreshCommand="{Binding RefreshCommand}"
IsRefreshing="{Binding IsRefreshing}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
在此示例中,当用户启动刷新时, ICommand 将执行 由 RefreshCommand 属性定义的 ,这会刷新正在显示的项目。 刷新时会显示刷新可视化效果,其中包含一个动画进度圆圈。 属性的值 IsRefreshing 指示刷新操作的当前状态。 触发刷新时,此属性将自动转换为 true。 刷新完成后,应将 属性重置为 false。