使用数据填充 CollectionView

浏览示例。 浏览示例

.NET Multi-platform App UI (.NET MAUI) CollectionView 包括下列属性,它们用于定义要显示的数据及其外观:

  • ItemsSource,类型为 IEnumerable,指定要显示的项集合,其默认值为 null
  • ItemTemplate,类型为 DataTemplate,指定将应用于要显示的项集合中的各个项的模板。

这些属性由 BindableProperty 对象提供支持,这意味着这些属性可以作为数据绑定的目标。

CollectionView 定义 ItemsUpdatingScrollMode 属性,表示 CollectionView 添加新项时的滚动行为。 有关此属性的详细信息,请参阅添加新项时的控件滚动位置

CollectionView 支持在用户滚动时进行增量数据虚拟化。 有关详细信息,请参阅以增量方式加载数据

使用数据填充 CollectionView

通过将 CollectionViewItemsSource 属性设置为实现 IEnumerable 的任何集合来为其填充数据。 默认情况下,CollectionView 在垂直列表中显示项。

重要

如果在基础集合中添加、移除或更改项时需要刷新 CollectionView,则基础集合应是发送属性更改通知的 IEnumerable 集合,例如 ObservableCollection

通过使用数据绑定将 CollectionViewItemsSource 属性绑定到 IEnumerable 集合即可对其填充数据。 在 XAML 中,此过程可通过使用 Binding 标记扩展实现:

<CollectionView ItemsSource="{Binding Monkeys}" />

等效 C# 代码如下:

CollectionView collectionView = new CollectionView();
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

在此示例中, ItemsSource 属性数据绑定到所连接视图模型的 Monkeys 属性。

注意

可以启用已编译的绑定,以提高 .NET MAUI 应用程序中的数据绑定性能。 有关详细信息,请参阅编译的绑定

有关如何更改 CollectionView 布局的信息,请参阅指定 CollectionView 布局。 有关如何定义 CollectionView 中各项外观的信息,请参阅定义项外观。 有关数据绑定的详细信息,请参阅数据绑定

警告

如果 ItemsSource 在 UI 线程之外更新,CollectionView 将触发异常。

定义项外观

CollectionView.ItemTemplate 属性设置为 DataTemplate 可定义 CollectionView 中每个项的外观:

<CollectionView ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <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>
        </DataTemplate>
    </CollectionView.ItemTemplate>
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView();
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

collectionView.ItemTemplate = new DataTemplate(() =>
{
    Grid grid = new Grid { Padding = 10 };
    grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
    grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
    grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
    grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

    Image image = new Image { Aspect = Aspect.AspectFill, HeightRequest = 60, WidthRequest = 60 };
    image.SetBinding(Image.SourceProperty, "ImageUrl");

    Label nameLabel = new Label { FontAttributes = FontAttributes.Bold };
    nameLabel.SetBinding(Label.TextProperty, "Name");

    Label locationLabel = new Label { FontAttributes = FontAttributes.Italic, VerticalOptions = LayoutOptions.End };
    locationLabel.SetBinding(Label.TextProperty, "Location");

    Grid.SetRowSpan(image, 2);

    grid.Add(image);
    grid.Add(nameLabel, 1, 0);
    grid.Add(locationLabel, 1, 1);

    return grid;
});

DataTemplate 中指定的元素定义列表中各个项的外观。 在此示例中,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; }
}

以下屏幕截图显示对列表中每个项进行模板化的结果:

屏幕截图显示了每个项都已模板化的 CollectionView。

提示

CollectionView 置于 VerticalStackLayout 中可以停止 CollectionView 滚动,并可以限制显示的项数。 在这种情况下,将 VerticalStackLayout 替换为 Grid

要详细了解数据模板,请参阅数据模板

在运行时选择项外观

通过将 CollectionView.ItemTemplate 属性设置为 DataTemplateSelector 对象,可以在运行时根据项值选择 CollectionView 中每个项的外观:

<ContentPage ...
             xmlns:controls="clr-namespace:CollectionViewDemos.Controls">
    <ContentPage.Resources>
        <DataTemplate x:Key="AmericanMonkeyTemplate">
            ...
        </DataTemplate>

        <DataTemplate x:Key="OtherMonkeyTemplate">
            ...
        </DataTemplate>

        <controls:MonkeyDataTemplateSelector x:Key="MonkeySelector"
                                             AmericanMonkey="{StaticResource AmericanMonkeyTemplate}"
                                             OtherMonkey="{StaticResource OtherMonkeyTemplate}" />
    </ContentPage.Resources>

    <CollectionView ItemsSource="{Binding Monkeys}"
                    ItemTemplate="{StaticResource MonkeySelector}" />
</ContentPage>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    ItemTemplate = new MonkeyDataTemplateSelector { ... }
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

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 类定义 AmericanMonkeyOtherMonkeyDataTemplate 属性,它们被设置为不同的数据模板。 当猴子名称包含“美国”时,OnSelectTemplate 重写函数会返回 AmericanMonkey 模板,该模板以青色显示猴子名称和位置。 当猴子名称中不包含“美国”时,OnSelectTemplate 重写函数将返回 OtherMonkey 模板,该模板以银色显示猴子名称和位置:

屏幕截图显示了 CollectionView 运行时项模板选择。

要详细了解数据模板选择器,请参阅创建 DataTemplateSelector

重要说明

使用 CollectionView 时,切勿将 DataTemplate 对象的根元素设置为 ViewCell。 这将引发异常,因为 CollectionView 没有单元格的概念。

上下文菜单

CollectionView 支持通过 SwipeView 查看上下文菜单的数据项,此控件通过轻扫手势显示上下文菜单。 SwipeView 是一个容器控件,该控件包围内容项,并为该内容项提供上下文菜单项。 因此,上下文菜单是通过创建用于定义 SwipeView 所围绕内容以及轻扫手势所显示上下文菜单项的 SwipeView,针对 CollectionView 实现的。 这可通过将 SwipeView 设置为 DataTemplate 中的根视图实现,该视图用于定义 CollectionView 中各数据项的外观:

<CollectionView x:Name="collectionView"
                ItemsSource="{Binding Monkeys}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <SwipeView>
                <SwipeView.LeftItems>
                    <SwipeItems>
                        <SwipeItem Text="Favorite"
                                   IconImageSource="favorite.png"
                                   BackgroundColor="LightGreen"
                                   Command="{Binding Source={x:Reference collectionView}, Path=BindingContext.FavoriteCommand}"
                                   CommandParameter="{Binding}" />
                        <SwipeItem Text="Delete"
                                   IconImageSource="delete.png"
                                   BackgroundColor="LightPink"
                                   Command="{Binding Source={x:Reference collectionView}, Path=BindingContext.DeleteCommand}"
                                   CommandParameter="{Binding}" />
                    </SwipeItems>
                </SwipeView.LeftItems>
                <Grid BackgroundColor="White"
                      Padding="10">
                    <!-- Define item appearance -->
                </Grid>
            </SwipeView>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView();
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");

collectionView.ItemTemplate = new DataTemplate(() =>
{
    // Define item appearance
    Grid grid = new Grid { Padding = 10, BackgroundColor = Colors.White };
    // ...

    SwipeView swipeView = new SwipeView();
    SwipeItem favoriteSwipeItem = new SwipeItem
    {
        Text = "Favorite",
        IconImageSource = "favorite.png",
        BackgroundColor = Colors.LightGreen
    };
    favoriteSwipeItem.SetBinding(MenuItem.CommandProperty, new Binding("BindingContext.FavoriteCommand", source: collectionView));
    favoriteSwipeItem.SetBinding(MenuItem.CommandParameterProperty, ".");

    SwipeItem deleteSwipeItem = new SwipeItem
    {
        Text = "Delete",
        IconImageSource = "delete.png",
        BackgroundColor = Colors.LightPink
    };
    deleteSwipeItem.SetBinding(MenuItem.CommandProperty, new Binding("BindingContext.DeleteCommand", source: collectionView));
    deleteSwipeItem.SetBinding(MenuItem.CommandParameterProperty, ".");

    swipeView.LeftItems = new SwipeItems { favoriteSwipeItem, deleteSwipeItem };
    swipeView.Content = grid;    
    return swipeView;
});

在此示例中,SwipeView 内容为定义 CollectionView 中各项外观的 Grid。 轻扫项用于对 SwipeView 内容执行操作,并在控件从左侧轻扫时显示:

屏幕截图显示了 CollectionView 上下文菜单项。

SwipeView 支持四个不同的轻扫方向,并且轻扫方向由添加 SwipeItems 对象的 SwipeItems 方向集合定义。 默认情况下,轻扫项会在用户点击时执行。 此外,执行某个轻扫项后,该轻扫项将会隐藏,并重新显示 SwipeView 内容。 但是,此类行为可以更改。

有关 SwipeView 控件的详细信息,请参阅 SwipeView

下拉以刷新

CollectionView 支持通过 RefreshView 实现下拉刷新功能,通过向下拉动项列表,可刷新显示的数据。 RefreshView 是一个容器控件,如果其子控件支持可滚动内容,则该控件可为其子控件提供下拉以刷新功能。 因此,通过将 CollectionView 设置为 RefreshView 的子级可实现其下拉以刷新功能:

<RefreshView IsRefreshing="{Binding IsRefreshing}"
             Command="{Binding RefreshCommand}">
    <CollectionView ItemsSource="{Binding Animals}">
        ...
    </CollectionView>
</RefreshView>

等效 C# 代码如下:

RefreshView refreshView = new RefreshView();
ICommand refreshCommand = new Command(() =>
{
    // IsRefreshing is true
    // Refresh data here
    refreshView.IsRefreshing = false;
});
refreshView.Command = refreshCommand;

CollectionView collectionView = new CollectionView();
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Animals");
refreshView.Content = collectionView;
// ...

当用户启动刷新时,将执行由 Command 属性定义的 ICommand,这将刷新所显示的项。 刷新时会显示刷新可视化效果,其中包含动画形式的圆形进度:

屏幕截图显示了 CollectionView 拉取刷新。

RefreshView.IsRefreshing 属性的值指示 RefreshView 的当前状态。 当用户触发刷新时,此属性将自动转换为 true。 刷新完成后,应将该属性重置为 false

有关 RefreshView 的详细信息,请参阅 RefreshView

以增量方式加载数据

CollectionView 支持在用户滚动时进行增量数据虚拟化。 这可实现诸如在用户滚动时从 Web 服务异步加载数据页等方案。 此外,加载更多数据的点是可配置的,这样用户就看不到空白,也不会停止滚动。

警告

请勿尝试在 StackLayout 中的 CollectionView 中以增量方式加载数据。 此方案将导致发生无限循环,CollectionView 将一直扩展。

CollectionView 定义下列属性,用于控制数据的增量加载:

  • RemainingItemsThreshold,类型为 int,列表中尚不可见的项的阈值,达到此阈值时将触发 RemainingItemsThresholdReached 事件。
  • RemainingItemsThresholdReachedCommand,类型为 ICommand,达到 RemainingItemsThreshold 时就会执行。
  • RemainingItemsThresholdReachedCommandParameter 属于 object 类型,是传递给 RemainingItemsThresholdReachedCommand 的参数。

CollectionView 还会定义一个 RemainingItemsThresholdReached 事件,当 CollectionView 滚动得足够远,直到尚未显示 RemainingItemsThreshold 项时,将触发该事件。 可以处理此事件以加载更多项。 此外,在触发 RemainingItemsThresholdReached 事件时,将执行 RemainingItemsThresholdReachedCommand,从而在视图模型中增量加载数据。

RemainingItemsThreshold 属性的默认值为 -1,指示永远不会触发 RemainingItemsThresholdReached 事件。 当属性值为 0 时,将在显示 ItemsSource 中的最后一项时触发 RemainingItemsThresholdReached 事件。 对于大于 0 的值,当 ItemsSource 包含尚未滚动到的项数时将触发 RemainingItemsThresholdReached 事件。

注意

CollectionView 用于验证 RemainingItemsThreshold 属性,使该属性的值始终大于或等于 -1。

下面的 XAML 示例显示了以增量方式加载数据的 CollectionView

<CollectionView ItemsSource="{Binding Animals}"
                RemainingItemsThreshold="5"
                RemainingItemsThresholdReached="OnCollectionViewRemainingItemsThresholdReached">
    ...
</CollectionView>

等效 C# 代码如下:

CollectionView collectionView = new CollectionView
{
    RemainingItemsThreshold = 5
};
collectionView.RemainingItemsThresholdReached += OnCollectionViewRemainingItemsThresholdReached;
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Animals");

在此代码示例中,RemainingItemsThresholdReached 事件在有 5 个项尚未滚动到时触发,并响应执行 OnCollectionViewRemainingItemsThresholdReached 事件处理程序:

void OnCollectionViewRemainingItemsThresholdReached(object sender, EventArgs e)
{
    // Retrieve more data here and add it to the CollectionView's ItemsSource collection.
}

注意

还可以通过在视图模型中将 RemainingItemsThresholdReachedCommand 绑定到 ICommand 实现,以增量方式加载数据。