Data and templates

Build reusable CollectionView cells and connect flat or grouped item sources.

CollectionView<TItem> renders a flat item source with native cell reuse and diffable updates. It is the normal choice for lists that can grow beyond a small, fixed set of views.

The item bindings below use the same property tracking described in MVVM and binding.

Content = new CollectionView<Contact>
{
    ItemsSource = viewModel.Contacts,
    ItemTemplate = static () => new ContactRow(),
    
    Layout = CollectionLayout.List(),
    
    EmptyView = new Label
    {
        HorizontalAlignment = HorizontalAlignment.Center,
        VerticalAlignment = VerticalAlignment.Center,
        Text = "No contacts"
    }
};

ItemsSource accepts an array, list, ObservableCollection<T>, collection expression, or list binding. An observable collection animates adds, removals, moves, and replacements into the native snapshot.

Item templates

ItemTemplate creates the element tree for a reusable cell. It is called for the cell, not for every item that passes through that cell. Bind its contents through ItemView<T>:

sealed class ContactRow : ItemView<Contact>
{
    readonly Border container;

    public ContactRow()
    {
        Content = container = new Border
        {
            Height = 56,
            Padding = new Thickness(16, 0),
            Child = new Label
            {
                VerticalAlignment = VerticalAlignment.Center,
                Text = Bind(contact => contact.Name)
            }
        };
    }

    protected override void OnItemChanged(Contact? contact)
    {
        container.Background = contact?.IsFavorite is true
            ? Colors.Yellow.WithAlpha(0.12)
            : null;
    }
}

Use bindings for item properties and OnItemChanged when the whole item affects structure or a non-bindable configuration. A recycled cell may receive many items, so the override must set both sides of every state instead of assuming the old item looked the same.

Item identity

Collection snapshots identify objects by reference. Keep the same model instances when moving or selecting them. Two records with equal values are still separate items; replacing one instance with another is a replacement, not an in-place update.

For property changes within a model, implement INotifyPropertyChanged and bind the cell. For membership and order changes, use ObservableCollection<T>.

Grouped data

CollectionView<TItem, TSection> accepts GroupedItemsSource, where TSection implements ISection<TItem>:

sealed record ContactSection(
    string Title,
    IReadOnlyList<Contact> Items) : ISection<Contact>;

Header and footer templates are ItemView<TSection> instances. GroupedItemsSource takes precedence over a flat ItemsSource when both are assigned.

Continue with layouts and sections for list, grid, carousel, and expandable-section behavior.