xaml-csharp-development-skill-for-avalonia

ItemsControl Virtualization and Recycling

Table of Contents

  1. Scope and APIs
  2. Virtualization Architecture
  3. Authoring Patterns
  4. Performance Practices
  5. Troubleshooting

Scope and APIs

Primary APIs:

Important members:

Reference source files:

Virtualization Architecture

Default high-level pipeline:

  1. ItemsControl owns item source and template.
  2. ItemsPresenter creates panel from ItemsPanel.
  3. If panel is VirtualizingPanel, it realizes only visible range.
  4. ItemContainerGenerator prepares/clears containers.
  5. Recyclable templates reduce container churn.

Authoring Patterns

Enable virtualizing panel and prefetch buffer

<ListBox ItemsSource="{Binding Rows}">
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <VirtualizingStackPanel Orientation="Vertical" CacheLength="1.0" />
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
</ListBox>

Recycling-friendly data template in C#

using Avalonia.Controls;
using Avalonia.Controls.Templates;

itemsControl.ItemTemplate = new FuncDataTemplate(
    match: item => item is RowViewModel,
    build: (_, _) => new RowView(),
    supportsRecycling: true);

Programmatic scroll to item

itemsControl.Presenter?.ScrollIntoView(index);

Custom virtualizing panel workflow

When deriving VirtualizingPanel, use ItemContainerGenerator in this order:

  1. NeedsContainer(...)
  2. CreateContainer(...) when needed
  3. PrepareItemContainer(...)
  4. Add via AddInternalChild(...) or InsertInternalChild(...)
  5. ItemContainerPrepared(...)
  6. On unrealize: ClearItemContainer(...)

Performance Practices

Troubleshooting

  1. List realizes too many items:
    • Non-virtualizing panel selected in ItemsPanel.
    • Nested layout constraints make viewport effectively infinite.
  2. Reuse artifacts (wrong visual state on scrolled items):
    • Recycled view state not reset in control/viewmodel.
    • Template assumes one-time initialization.
  3. ContainerFromIndex returns null unexpectedly:
    • Item is not currently realized.
    • Use ScrollIntoView(...) first.
  4. Jank on fast scroll:
    • Item templates allocate heavily.
    • CacheLength too small for workload, causing churn.

XAML-First and Code-Only Usage

Default mode:

XAML-first complete example:

<ListBox xmlns="https://github.com/avaloniaui"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:vm="using:MyApp.ViewModels"
         x:DataType="vm:ListPageViewModel"
         ItemsSource="{CompiledBinding Rows}">
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <VirtualizingStackPanel CacheLength="1.0" />
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>

  <ListBox.ItemTemplate>
    <DataTemplate x:DataType="vm:RowViewModel">
      <TextBlock Text="{CompiledBinding Title}" />
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

Code-only alternative (on request):

using Avalonia.Controls.Templates;

listBox.ItemsSource = viewModel.Rows;
listBox.ItemTemplate = new FuncDataTemplate<RowViewModel>((row, _) =>
    new TextBlock { Text = row.Title },
    supportsRecycling: true);

listBox.Presenter?.ScrollIntoView(0);