maui-collectionview

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CollectionView — .NET MAUI

CollectionView — .NET MAUI

CollectionView
is the primary control for displaying scrollable lists and grids of data in .NET MAUI. It replaces
ListView
with better performance, flexible layouts, and no
ViewCell
requirement.
CollectionView
是.NET MAUI中用于展示可滚动数据列表和网格的核心控件。它替代了
ListView
,具备更优的性能、灵活的布局,且无需使用
ViewCell

When to Use

适用场景

  • Displaying a scrollable list or grid of data items
  • Binding a collection of objects to a templated item layout
  • Adding selection (single or multiple), grouping, or pull-to-refresh
  • Implementing infinite scroll / incremental loading
  • Showing swipe actions on list items
  • Displaying an empty state when no data is available
  • 展示可滚动的数据列表或网格
  • 将对象集合绑定到带模板的项布局
  • 添加选择(单选或多选)、分组或下拉刷新功能
  • 实现无限滚动/增量加载
  • 为列表项添加滑动操作
  • 无数据时展示空状态

When Not to Use

不适用场景

  • Static layouts with a fixed number of items — use
    Grid
    or
    StackLayout
    directly
  • Map pin lists — use the
    Microsoft.Maui.Controls.Maps
    NuGet package
  • Table-based data entry forms — use standard form controls
  • Simple text-only lists with no interaction — consider
    BindableLayout
    on a
    StackLayout
  • 固定数量项的静态布局——直接使用
    Grid
    StackLayout
  • 地图标记列表——使用
    Microsoft.Maui.Controls.Maps
    NuGet包
  • 基于表格的数据输入表单——使用标准表单控件
  • 无交互的纯文本简单列表——考虑在
    StackLayout
    上使用
    BindableLayout

Inputs

输入项

  • A data source (typically
    ObservableCollection<T>
    ) bound to
    ItemsSource
  • A
    DataTemplate
    defining how each item renders
  • Optional: layout configuration, selection mode, grouping model, empty view
  • 绑定到
    ItemsSource
    的数据源(通常为
    ObservableCollection<T>
  • 定义每个项渲染方式的
    DataTemplate
  • 可选:布局配置、选择模式、分组模型、空视图

Basic Setup

基础设置

xml
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="models:Item">
            <HorizontalStackLayout Padding="8" Spacing="8">
                <Image Source="{Binding Icon}" WidthRequest="40" HeightRequest="40" />
                <Label Text="{Binding Name}" VerticalOptions="Center" />
            </HorizontalStackLayout>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
Key rules:
  • Bind
    ItemsSource
    to an
    ObservableCollection<T>
    so the UI updates on add/remove.
  • Each item template root must be a
    Layout
    or
    View
    never use
    ViewCell
    .
  • Always set
    x:DataType
    on
    DataTemplate
    for compiled bindings.
xml
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="models:Item">
            <HorizontalStackLayout Padding="8" Spacing="8">
                <Image Source="{Binding Icon}" WidthRequest="40" HeightRequest="40" />
                <Label Text="{Binding Name}" VerticalOptions="Center" />
            </HorizontalStackLayout>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
核心规则:
  • ItemsSource
    绑定到
    ObservableCollection<T>
    ,以便在添加/移除项时自动更新UI。
  • 每个项模板的根元素必须是
    Layout
    View
    ——切勿使用
    ViewCell
  • 始终为
    DataTemplate
    设置
    x:DataType
    以启用编译绑定。

Layouts

布局设置

Set
ItemsLayout
to control arrangement. Default is
VerticalList
.
LayoutXAML value
Vertical list
VerticalList
(default)
Horizontal list
HorizontalList
Vertical grid
GridItemsLayout
with
Orientation="Vertical"
Horizontal grid
GridItemsLayout
with
Orientation="Horizontal"
设置
ItemsLayout
来控制项的排列方式,默认值为
VerticalList
布局类型XAML 值
垂直列表
VerticalList
(默认)
水平列表
HorizontalList
垂直网格
GridItemsLayout
搭配
Orientation="Vertical"
水平网格
GridItemsLayout
搭配
Orientation="Horizontal"

Grid Layout

网格布局

xml
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemsLayout>
        <GridItemsLayout Orientation="Vertical"
                         Span="2"
                         VerticalItemSpacing="8"
                         HorizontalItemSpacing="8" />
    </CollectionView.ItemsLayout>
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="models:Item">
            <Border Padding="8" StrokeThickness="0">
                <VerticalStackLayout>
                    <Image Source="{Binding Image}" HeightRequest="120" Aspect="AspectFill" />
                    <Label Text="{Binding Name}" FontAttributes="Bold" />
                </VerticalStackLayout>
            </Border>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
xml
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemsLayout>
        <GridItemsLayout Orientation="Vertical"
                         Span="2"
                         VerticalItemSpacing="8"
                         HorizontalItemSpacing="8" />
    </CollectionView.ItemsLayout>
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="models:Item">
            <Border Padding="8" StrokeThickness="0">
                <VerticalStackLayout>
                    <Image Source="{Binding Image}" HeightRequest="120" Aspect="AspectFill" />
                    <Label Text="{Binding Name}" FontAttributes="Bold" />
                </VerticalStackLayout>
            </Border>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Horizontal List

水平列表

xml
<CollectionView ItemsSource="{Binding Items}"
                ItemsLayout="HorizontalList" />
xml
<CollectionView ItemsSource="{Binding Items}"
                ItemsLayout="HorizontalList" />

Selection

选择功能

Selection Mode

选择模式

ModeProperty to bindBinding mode
None
Single
SelectedItem
TwoWay
Multiple
SelectedItems
OneWay
xml
<CollectionView ItemsSource="{Binding Items}"
                SelectionMode="Single"
                SelectedItem="{Binding CurrentItem, Mode=TwoWay}"
                SelectionChangedCommand="{Binding ItemSelectedCommand}" />
For
Multiple
selection, bind
SelectedItems
(type
IList<object>
):
xml
<CollectionView SelectionMode="Multiple"
                SelectedItems="{Binding ChosenItems, Mode=OneWay}" />
模式绑定属性绑定模式
None
Single
SelectedItem
TwoWay
Multiple
SelectedItems
OneWay
xml
<CollectionView ItemsSource="{Binding Items}"
                SelectionMode="Single"
                SelectedItem="{Binding CurrentItem, Mode=TwoWay}"
                SelectionChangedCommand="{Binding ItemSelectedCommand}" />
对于
Multiple
多选模式,绑定
SelectedItems
(类型为
IList<object>
):
xml
<CollectionView SelectionMode="Multiple"
                SelectedItems="{Binding ChosenItems, Mode=OneWay}" />

Selected Visual State

选中视觉状态

Highlight selected items using
VisualStateManager
:
xml
<CollectionView.ItemTemplate>
    <DataTemplate x:DataType="models:Item">
        <Grid Padding="8">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="CommonStates">
                    <VisualState Name="Normal">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="Transparent" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="Selected">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor"
                                    Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Label Text="{Binding Name}" />
        </Grid>
    </DataTemplate>
</CollectionView.ItemTemplate>
使用
VisualStateManager
高亮选中项:
xml
<CollectionView.ItemTemplate>
    <DataTemplate x:DataType="models:Item">
        <Grid Padding="8">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="CommonStates">
                    <VisualState Name="Normal">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="Transparent" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="Selected">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor"
                                    Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Label Text="{Binding Name}" />
        </Grid>
    </DataTemplate>
</CollectionView.ItemTemplate>

Grouping

分组功能

  1. Create a group class inheriting from
    List<T>
    :
csharp
public class AnimalGroup : List<Animal>
{
    public string Name { get; }
    public AnimalGroup(string name, List<Animal> animals) : base(animals)
    {
        Name = name;
    }
}
  1. Bind to
    ObservableCollection<AnimalGroup>
    and set
    IsGrouped="True"
    :
xml
<CollectionView ItemsSource="{Binding AnimalGroups}"
                IsGrouped="True">
    <CollectionView.GroupHeaderTemplate>
        <DataTemplate x:DataType="models:AnimalGroup">
            <Label Text="{Binding Name}"
                   FontAttributes="Bold"
                   BackgroundColor="{StaticResource Gray100}"
                   Padding="8" />
        </DataTemplate>
    </CollectionView.GroupHeaderTemplate>
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="models:Animal">
            <Label Text="{Binding Name}" Padding="16,4" />
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
  1. 创建继承自
    List<T>
    的分组类:
csharp
public class AnimalGroup : List<Animal>
{
    public string Name { get; }
    public AnimalGroup(string name, List<Animal> animals) : base(animals)
    {
        Name = name;
    }
}
  1. 绑定到
    ObservableCollection<AnimalGroup>
    并设置
    IsGrouped="True"
xml
<CollectionView ItemsSource="{Binding AnimalGroups}"
                IsGrouped="True">
    <CollectionView.GroupHeaderTemplate>
        <DataTemplate x:DataType="models:AnimalGroup">
            <Label Text="{Binding Name}"
                   FontAttributes="Bold"
                   BackgroundColor="{StaticResource Gray100}"
                   Padding="8" />
        </DataTemplate>
    </CollectionView.GroupHeaderTemplate>
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="models:Animal">
            <Label Text="{Binding Name}" Padding="16,4" />
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Pull-to-Refresh

下拉刷新

Wrap
CollectionView
in a
RefreshView
. Set
IsRefreshing
back to
false
when done:
xml
<RefreshView IsRefreshing="{Binding IsRefreshing}"
             Command="{Binding RefreshCommand}">
    <CollectionView ItemsSource="{Binding Items}" />
</RefreshView>
CollectionView
包裹在
RefreshView
中,完成刷新后将
IsRefreshing
设回
false
xml
<RefreshView IsRefreshing="{Binding IsRefreshing}"
             Command="{Binding RefreshCommand}">
    <CollectionView ItemsSource="{Binding Items}" />
</RefreshView>

Incremental Loading (Infinite Scroll)

增量加载(无限滚动)

xml
<CollectionView ItemsSource="{Binding Items}"
                RemainingItemsThreshold="5"
                RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}" />
⚠️ Do NOT use with non-virtualizing layouts.
LinearItemsLayout
and
GridItemsLayout
support virtualization. Using
BindableLayout
on a
StackLayout
as an alternative to
CollectionView
has no virtualization, which triggers infinite threshold-reached events.
xml
<CollectionView ItemsSource="{Binding Items}"
                RemainingItemsThreshold="5"
                RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}" />
⚠️ 请勿与非虚拟化布局搭配使用。
LinearItemsLayout
GridItemsLayout
支持虚拟化。若使用
StackLayout
上的
BindableLayout
替代
CollectionView
,则无虚拟化功能,会触发无限次的阈值到达事件。

SwipeView — Binding from Inside DataTemplate

SwipeView — 从DataTemplate内部绑定

Commands inside a
DataTemplate
can't directly reach your ViewModel. Use
RelativeSource AncestorType
:
xml
<CollectionView.ItemTemplate>
    <DataTemplate x:DataType="models:Item">
        <SwipeView>
            <SwipeView.RightItems>
                <SwipeItems>
                    <SwipeItem Text="Delete"
                               BackgroundColor="Red"
                               Command="{Binding BindingContext.DeleteCommand, Source={RelativeSource AncestorType={x:Type ContentPage}}}"
                               CommandParameter="{Binding}" />
                </SwipeItems>
            </SwipeView.RightItems>
            <Grid Padding="8">
                <Label Text="{Binding Name}" />
            </Grid>
        </SwipeView>
    </DataTemplate>
</CollectionView.ItemTemplate>
DataTemplate
内部的命令无法直接访问ViewModel,需使用
RelativeSource AncestorType
xml
<CollectionView.ItemTemplate>
    <DataTemplate x:DataType="models:Item">
        <SwipeView>
            <SwipeView.RightItems>
                <SwipeItems>
                    <SwipeItem Text="Delete"
                               BackgroundColor="Red"
                               Command="{Binding BindingContext.DeleteCommand, Source={RelativeSource AncestorType={x:Type ContentPage}}}"
                               CommandParameter="{Binding}" />
                </SwipeItems>
            </SwipeView.RightItems>
            <Grid Padding="8">
                <Label Text="{Binding Name}" />
            </Grid>
        </SwipeView>
    </DataTemplate>
</CollectionView.ItemTemplate>

EmptyView

空视图

Shown when
ItemsSource
is empty or null.
xml
<CollectionView ItemsSource="{Binding SearchResults}"
                EmptyView="No items found." />
For a custom empty view, wrap in
ContentView
:
xml
<CollectionView ItemsSource="{Binding SearchResults}">
    <CollectionView.EmptyView>
        <ContentView>
            <VerticalStackLayout HorizontalOptions="Center" VerticalOptions="Center">
                <Image Source="empty_state.png" WidthRequest="120" />
                <Label Text="Nothing here yet" HorizontalTextAlignment="Center" />
            </VerticalStackLayout>
        </ContentView>
    </CollectionView.EmptyView>
</CollectionView>
ItemsSource
为空或null时展示:
xml
<CollectionView ItemsSource="{Binding SearchResults}"
                EmptyView="No items found." />
如需自定义空视图,可包裹在
ContentView
中:
xml
<CollectionView ItemsSource="{Binding SearchResults}">
    <CollectionView.EmptyView>
        <ContentView>
            <VerticalStackLayout HorizontalOptions="Center" VerticalOptions="Center">
                <Image Source="empty_state.png" WidthRequest="120" />
                <Label Text="Nothing here yet" HorizontalTextAlignment="Center" />
            </VerticalStackLayout>
        </ContentView>
    </CollectionView.EmptyView>
</CollectionView>

Headers and Footers

页眉与页脚

xml
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.Header>
        <Label Text="Header" FontAttributes="Bold" Padding="8" />
    </CollectionView.Header>
    <CollectionView.Footer>
        <Label Text="Footer" FontAttributes="Italic" Padding="8" />
    </CollectionView.Footer>
</CollectionView>
Use
HeaderTemplate
/
FooterTemplate
when headers or footers are data-bound.
xml
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.Header>
        <Label Text="Header" FontAttributes="Bold" Padding="8" />
    </CollectionView.Header>
    <CollectionView.Footer>
        <Label Text="Footer" FontAttributes="Italic" Padding="8" />
    </CollectionView.Footer>
</CollectionView>
当页眉或页脚需要数据绑定时,使用
HeaderTemplate
/
FooterTemplate

Scrolling

滚动功能

ScrollTo

滚动到指定位置

Programmatically scroll by index or item:
csharp
// Scroll to index
collectionView.ScrollTo(index: 10, position: ScrollToPosition.Center, animate: true);

// Scroll to item
collectionView.ScrollTo(item: myItem, position: ScrollToPosition.MakeVisible, animate: true);
ScrollToPositionBehavior
MakeVisible
Scrolls just enough to make the item visible
Start
Scrolls item to the start of the viewport
Center
Scrolls item to the center of the viewport
End
Scrolls item to the end of the viewport
通过索引或项编程式滚动:
csharp
// 滚动到指定索引
collectionView.ScrollTo(index: 10, position: ScrollToPosition.Center, animate: true);

// 滚动到指定项
collectionView.ScrollTo(item: myItem, position: ScrollToPosition.MakeVisible, animate: true);
ScrollToPosition行为
MakeVisible
滚动至刚好能显示该项的位置
Start
滚动至该项位于视口起始处
Center
滚动至该项位于视口中央
End
滚动至该项位于视口末尾

Snap Points

吸附点

xml
<CollectionView.ItemsLayout>
    <LinearItemsLayout Orientation="Horizontal"
                       SnapPointsType="MandatorySingle"
                       SnapPointsAlignment="Center" />
</CollectionView.ItemsLayout>
  • SnapPointsType
    :
    None
    ,
    Mandatory
    ,
    MandatorySingle
  • SnapPointsAlignment
    :
    Start
    ,
    Center
    ,
    End
xml
<CollectionView.ItemsLayout>
    <LinearItemsLayout Orientation="Horizontal"
                       SnapPointsType="MandatorySingle"
                       SnapPointsAlignment="Center" />
</CollectionView.ItemsLayout>
  • SnapPointsType
    :
    None
    ,
    Mandatory
    ,
    MandatorySingle
  • SnapPointsAlignment
    :
    Start
    ,
    Center
    ,
    End

Performance Tips

性能优化技巧

  • Use
    MeasureFirstItem
    for uniform item sizes — significantly faster than
    MeasureAllItems
    :
    xml
    <LinearItemsLayout Orientation="Vertical" ItemSizingStrategy="MeasureFirstItem" />
  • Always use
    ObservableCollection<T>
    , not
    List<T>
    . Swapping a
    List
    forces a full re-render.
  • Update collections on the UI thread
    MainThread.BeginInvokeOnMainThread(() => Items.Add(item))
    .
  • 使用
    MeasureFirstItem
    :对于尺寸统一的项,此策略比
    MeasureAllItems
    快得多:
    xml
    <LinearItemsLayout Orientation="Vertical" ItemSizingStrategy="MeasureFirstItem" />
  • 始终使用
    ObservableCollection<T>
    ,而非
    List<T>
    。替换
    List
    会触发完整的重新渲染。
  • 在UI线程更新集合——使用
    MainThread.BeginInvokeOnMainThread(() => Items.Add(item))

Common Pitfalls

常见陷阱

IssueFix
UI doesn't update when items changeUse
ObservableCollection<T>
, not
List<T>
.
App crashes or blank itemsNever use
ViewCell
— use
Grid
,
StackLayout
, or any
View
as template root.
Items disappear or layout breaksAlways update
ItemsSource
and the collection on the UI thread (
MainThread.BeginInvokeOnMainThread
).
Incremental loading fires endlesslyDon't use
StackLayout
as layout; use
LinearItemsLayout
or
GridItemsLayout
.
EmptyView doesn't render correctlyWrap custom empty views in
ContentView
.
Poor scroll performanceUse
MeasureFirstItem
sizing strategy for uniform item sizes.
Selected state not visibleAdd
VisualState Name="Selected"
to the item template root element.
Binding errors in SwipeView commandsUse
RelativeSource AncestorType
to reach the ViewModel from inside the item template.
Using ListView instead of CollectionView
CollectionView
replaces
ListView
— it has better performance, no
ViewCell
, and flexible layouts.
问题解决方法
项变更时UI未更新使用
ObservableCollection<T>
,而非
List<T>
应用崩溃或项显示空白切勿使用
ViewCell
——使用
Grid
StackLayout
或任意
View
作为模板根元素。
项消失或布局错乱始终在UI线程更新
ItemsSource
和集合(使用
MainThread.BeginInvokeOnMainThread
)。
增量加载无限触发不要使用
StackLayout
作为布局;使用
LinearItemsLayout
GridItemsLayout
空视图渲染异常将自定义空视图包裹在
ContentView
中。
滚动性能差对于尺寸统一的项,使用
MeasureFirstItem
尺寸策略。
选中状态不可见为项模板根元素添加
VisualState Name="Selected"
SwipeView命令绑定出错使用
RelativeSource AncestorType
从项模板内部访问ViewModel。
仍在使用ListView而非CollectionView
CollectionView
已替代
ListView
——它性能更优、无需
ViewCell
且布局灵活。

References

参考资料