askill
maui-collectionview

maui-collectionviewSafety 100Repository

Guidance for implementing CollectionView in .NET MAUI apps — data display, layouts (list & grid), selection, grouping, scrolling, empty views, templates, incremental loading, swipe actions, and pull-to-refresh. USE FOR: "CollectionView", "list view", "grid layout", "data template", "item template", "grouping", "pull to refresh", "incremental loading", "swipe actions", "empty view", "selection mode", "scroll to item". DO NOT USE FOR: simple static layouts (use maui-data-binding), map pin lists (use maui-maps), or table-based data entry forms.

43 stars
1.2k downloads
Updated 2/19/2026

Package Files

Loading files...
SKILL.md

CollectionView – .NET MAUI

Use CollectionView for displaying scrollable lists and grids of data. It replaces ListView and offers better performance, flexible layouts, and no ViewCell requirement.

Basic setup

<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>
  • Bind ItemsSource to an ObservableCollection<T> so the UI updates on add/remove.
  • Each item template root must be a Layout or Viewnever use ViewCell.
  • Always set x:DataType on DataTemplate for compiled bindings.

DataTemplateSelector

Choose templates at runtime based on item properties:

public class ItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate NormalTemplate { get; set; }
    public DataTemplate HighlightTemplate { get; set; }

    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        return ((Item)item).IsHighlighted ? HighlightTemplate : NormalTemplate;
    }
}
<CollectionView ItemsSource="{Binding Items}"
                ItemTemplate="{StaticResource MyTemplateSelector}" />

Layouts

Set ItemsLayout to control arrangement. Default is VerticalList.

LayoutXAML value
Vertical listVerticalList (default)
Horizontal listHorizontalList
Vertical gridGridItemsLayout with Orientation="Vertical"
Horizontal gridGridItemsLayout with Orientation="Horizontal"

Grid layout

<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemsLayout>
        <GridItemsLayout Orientation="Vertical"
                         Span="2"
                         VerticalItemSpacing="8"
                         HorizontalItemSpacing="8" />
    </CollectionView.ItemsLayout>
</CollectionView>

Horizontal list

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

ItemSizingStrategy

Controls how items are measured. Set on ItemsLayout.

ValueBehavior
MeasureAllItemsMeasures every item individually (default). Accurate but slower for heterogeneous sizes.
MeasureFirstItemMeasures only the first item and applies that size to all. Much faster for uniform items.
<CollectionView.ItemsLayout>
    <LinearItemsLayout Orientation="Vertical"
                       ItemSizingStrategy="MeasureFirstItem" />
</CollectionView.ItemsLayout>

ItemSpacing

Use ItemSpacing on LinearItemsLayout or VerticalItemSpacing / HorizontalItemSpacing on GridItemsLayout:

<CollectionView.ItemsLayout>
    <LinearItemsLayout Orientation="Vertical" ItemSpacing="8" />
</CollectionView.ItemsLayout>

Headers and footers

Supports string, view, or templated:

<!-- Simple string -->
<CollectionView Header="My Items" Footer="End of list" />

<!-- Custom view -->
<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/footers are data-bound.

Selection

Selection mode

ModeProperty to bindBinding mode
None
SingleSelectedItemTwoWay
MultipleSelectedItemsOneWay
<CollectionView ItemsSource="{Binding Items}"
                SelectionMode="Single"
                SelectedItem="{Binding CurrentItem, Mode=TwoWay}"
                SelectionChangedCommand="{Binding ItemSelectedCommand}" />

For Multiple selection, bind SelectedItems (type IList<object>):

<CollectionView SelectionMode="Multiple"
                SelectedItems="{Binding ChosenItems, Mode=OneWay}" />

Selected visual state

Highlight selected items using VisualStateManager:

<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>:
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":
<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.GroupFooterTemplate>
        <DataTemplate x:DataType="models:AnimalGroup">
            <Label Text="{Binding Count, StringFormat='{0} items'}"
                   FontAttributes="Italic"
                   Padding="4,0" />
        </DataTemplate>
    </CollectionView.GroupFooterTemplate>
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="models:Animal">
            <Label Text="{Binding Name}" Padding="16,4" />
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

EmptyView

Shown when ItemsSource is empty or null.

<!-- Simple string -->
<CollectionView EmptyView="No items found." />

<!-- Custom view — wrap in a ContentView -->
<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>

You can also use EmptyViewTemplate with a DataTemplateSelector to swap empty views based on state.

Gotcha: When using a custom view for EmptyView, wrap it in a ContentView to avoid layout issues.

Scrolling

ScrollTo

Programmatically scroll by index or item:

// 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
MakeVisibleScrolls just enough to make the item visible
StartScrolls item to the start of the viewport
CenterScrolls item to the center of the viewport
EndScrolls item to the end of the viewport

Snap points

Control snap behavior after scrolling:

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

Incremental loading (infinite scroll)

Load more data as the user scrolls near the end:

<CollectionView ItemsSource="{Binding Items}"
                RemainingItemsThreshold="5"
                RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}" />

Or handle the RemainingItemsThresholdReached event in code-behind.

Gotcha: Do not use incremental loading with a StackLayout-based ItemsLayout. A StackLayout has no concept of virtualization and will trigger infinite threshold-reached events. Use the default LinearItemsLayout or GridItemsLayout.

SwipeView integration

Wrap item content in SwipeView for swipe actions:

<CollectionView.ItemTemplate>
    <DataTemplate x:DataType="models:Item">
        <SwipeView>
            <SwipeView.RightItems>
                <SwipeItems>
                    <SwipeItem Text="Delete"
                               BackgroundColor="Red"
                               Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:MainViewModel}}, Path=DeleteCommand}"
                               CommandParameter="{Binding}" />
                </SwipeItems>
            </SwipeView.RightItems>
            <Grid Padding="12">
                <Label Text="{Binding Name}" />
            </Grid>
        </SwipeView>
    </DataTemplate>
</CollectionView.ItemTemplate>

Pull-to-refresh with RefreshView

Wrap CollectionView in a RefreshView:

<RefreshView IsRefreshing="{Binding IsRefreshing}"
             Command="{Binding RefreshCommand}">
    <CollectionView ItemsSource="{Binding Items}">
        <!-- ItemTemplate -->
    </CollectionView>
</RefreshView>

Set IsRefreshing back to false in the view model when the refresh completes.

Common gotchas

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 view model from inside the item template.

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

94/100Analyzed 2/23/2026

Excellent SKILL.md for .NET MAUI CollectionView. Comprehensive reference covering all major features (layouts, selection, grouping, scrolling, templates, incremental loading, swipe actions, pull-to-refresh) with extensive code examples and tables. Includes clear USE FOR / DO NOT USE FOR guidance and common gotchas. Reference-style content that applies to any MAUI project. Minor improvement: add tags for discoverability.

100
95
85
95
95

Metadata

Licenseunknown
Version-
Updated2/19/2026
Publisherdavidortinau

Tags

No tags yet.