Controls

ItemsControl

ItemsControl is the framework's non-selecting item repeater. It turns either inline Items or a bound ItemsSource into a vertical sequence of realized child elements, optionally using data templates, template selection, item container styling, and grouped views.

Quick start

Use ItemsControl when you want to render a collection but do not need built-in selection, current item handling, or row-level interaction semantics. If you need selection, use controls derived from it such as ListBox, ListView, TreeView, or ComboBox.

Inline items

<ItemsControl>
  <TextBlock Text="Alpha" />
  <TextBlock Text="Bravo" />
  <TextBlock Text="Charlie" />
</ItemsControl>

Bound list with an item template

<ItemsControl ItemsSource="{Binding Notifications}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Border Background="#172331"
              BorderBrush="#2E4A66"
              BorderThickness="1"
              Padding="10"
              Margin="0,0,0,8">
        <StackPanel>
          <TextBlock Text="{Binding Title}" Margin="0,0,0,4" />
          <TextBlock Text="{Binding Message}" />
        </StackPanel>
      </Border>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

CollectionView-backed list

<UserControl.Resources>
  <CollectionViewSource x:Key="BuildsView">
    <CollectionViewSource.SortDescriptions>
      <SortDescription PropertyName="StartedAt" Direction="Descending" />
    </CollectionViewSource.SortDescriptions>
  </CollectionViewSource>
</UserControl.Resources>

<ItemsControl ItemsSource="{StaticResource BuildsView}" />

Default mental model: ItemsControl is a vertical renderer for a collection. Every realized item is measured and arranged in order from top to bottom.

Content model and item sources

ItemsControl supports two mutually exclusive ways to provide data.

Items

Use Items for local, directly owned entries. In XAML, inline child elements are added to this collection automatically.

ItemsSource

Use ItemsSource to bind an external collection or a CollectionViewSource. Once set, the Items collection becomes read-only for mutation.

  • Inline child elements in XAML become entries in Items.
  • If ItemsSource is set, modifying Items throws an InvalidOperationException.
  • If you want to switch back to local items, clear ItemsSource first.
  • Do not mix ItemsSource with inline child items in the same element.

Local items created in code

var items = new ItemsControl();
items.Items.Add("Queued");
items.Items.Add("Running");
items.Items.Add("Completed");

Switching back from ItemsSource to local items

itemsControl.ItemsSource = null;
itemsControl.Items.Add("Manual item");

How item generation works

For each projected item, the control chooses or creates one realized visual element. The resolution order matters because it determines whether you get a template result, a reused UI element, or an automatically generated fallback container.

1. Explicit template selection

If ItemTemplate or ItemTemplateSelector is set, the control resolves a template first and builds from that when possible.

2. Implicit template lookup

If no explicit template is set, the control looks for an implicit DataTemplate for the item's runtime type.

3. UI element passthrough

If the item is already a UIElement and no template handled it, that element is used directly as the realized item.

4. Fallback container

If the item is plain data and no template exists, the control creates a Label whose text is item.ToString().

Direct UI elements

<ItemsControl>
  <Border Background="#1C2B3B" BorderBrush="#35516B" BorderThickness="1" Padding="8">
    <TextBlock Text="Already a UIElement" />
  </Border>
    <Button Content="Second element" />
</ItemsControl>

Fallback text generation for plain objects

var items = new ItemsControl();
items.Items.Add(42);
items.Items.Add(DateTime.Today);
items.Items.Add(new BuildStep("Restore"));

Without a matching template, those entries render through generated Label containers using the object's string representation.

Templates and styling

ItemsControl exposes the core item templating surface you use throughout the framework.

ItemTemplate

Use one DataTemplate when every item should render the same way.

ItemTemplateSelector

Use a selector when rendering depends on the data item at runtime.

ItemContainerStyle

Applies a Style to generated item containers when those containers are FrameworkElement instances.

Implicit templates

If no explicit template is provided, typed templates can still be resolved implicitly from resources.

Single template for a bound row model

<ItemsControl ItemsSource="{Binding BuildJobs}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Border Background="#172331"
              BorderBrush="#2B4259"
              BorderThickness="1"
              Padding="12"
              Margin="0,0,0,8">
        <Grid>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
          </Grid.ColumnDefinitions>
          <TextBlock Grid.Column="0" Text="{Binding Name}" />
          <TextBlock Grid.Column="1" Text="{Binding Status}" />
        </Grid>
      </Border>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

Applying shared chrome to generated containers

<ItemsControl ItemsSource="{Binding Steps}">
  <ItemsControl.ItemContainerStyle>
    <Style TargetType="{x:Type Label}">
      <Setter Property="Margin" Value="0,0,0,6" />
      <Setter Property="Padding" Value="8,4" />
      <Setter Property="Background" Value="#1B2938" />
      <Setter Property="BorderBrush" Value="#35516B" />
      <Setter Property="BorderThickness" Value="1" />
    </Style>
  </ItemsControl.ItemContainerStyle>
</ItemsControl>

That pattern is most useful when the control is auto-generating fallback containers such as Label. If your item template builds the full visuals itself, style the root element inside the template instead.

Using an item template selector

<ItemsControl ItemsSource="{Binding Feed}"
              ItemTemplateSelector="{StaticResource FeedTemplateSelector}" />

Layout behavior

The built-in layout behavior is intentionally simple. Whether items are hosted directly by the control or by an ItemsPresenter, realized items are measured with the available size and then arranged in a single top-to-bottom stack.

  • Desired width is the maximum desired width of all realized items.
  • Desired height is the sum of all realized item heights.
  • Each item is arranged at the full available width of the control's final slot.
  • The default item flow is vertical only.

No ItemsPanel abstraction here: this implementation does not expose WPF's ItemsPanel property. If you need a different collection layout pattern, use a more specialized control or build a control template that changes the surrounding composition, not the core item flow contract.

Collection views, sorting, filtering, and grouping

ItemsSource can point at a collection directly, but the richer pattern is to bind a CollectionViewSource. That gives the control a projected view that can sort, filter, and group the source data before it is realized.

Sorted view source

<UserControl.Resources>
  <CollectionViewSource x:Key="BuildsView">
    <CollectionViewSource.SortDescriptions>
      <SortDescription PropertyName="StartedAt" Direction="Descending" />
    </CollectionViewSource.SortDescriptions>
  </CollectionViewSource>
</UserControl.Resources>

<ItemsControl ItemsSource="{StaticResource BuildsView}" />

Grouped view source

<UserControl.Resources>
  <CollectionViewSource x:Key="TasksView">
    <CollectionViewSource.GroupDescriptions>
      <PropertyGroupDescription PropertyName="Category" />
    </CollectionViewSource.GroupDescriptions>
  </CollectionViewSource>
</UserControl.Resources>

<ItemsControl ItemsSource="{StaticResource TasksView}">
  <ItemsControl.GroupStyle>
    <GroupStyle HeaderStringFormat="{0}" />
  </ItemsControl.GroupStyle>
</ItemsControl>

When grouping is active and the control has at least one GroupStyle, it projects grouped visuals using GroupItem containers. Each group header defaults to Name (ItemCount) unless you provide HeaderStringFormat or a HeaderTemplate.

CollectionViewSource features

Supports SortDescriptions, GroupDescriptions, and a Filter predicate on the produced view.

GroupStyle features

The current API exposes HeaderTemplate and HeaderStringFormat for group headers.

Working with updates

ItemsControl listens to collection change notifications and updates realized children incrementally for add, remove, replace, and move operations when it can. That keeps existing containers stable and lets bindings such as RelativeSourceMode.PreviousData re-evaluate against the current projected order.

Observable collection update flow

public ObservableCollection<BuildStep> Steps { get; } =
[
    new("Restore"),
    new("Build"),
    new("Test")
];

Steps.Insert(1, new BuildStep("Generate Assets"));
Steps.Move(0, 2);
Steps.RemoveAt(1);

Those operations update the realized item sequence without requiring you to recreate the entire control tree yourself.

Property reference

Item source properties

Items: the local item collection owned by the control.

ItemsSource: external collection or CollectionViewSource used to project items.

GroupStyle: collection of GroupStyle descriptors used when the active view is grouped.

Templating properties

ItemTemplate: explicit template used for every item.

ItemTemplateSelector: selector used to choose a template per item.

ItemContainerStyle: style applied to generated framework-element containers when the control owns that style assignment.

Standard inherited properties such as Margin, Width, Height, HorizontalAlignment, VerticalAlignment, Opacity, and control-level styling properties continue to work as usual because ItemsControl derives from Control.

Patterns and examples

Settings list with static entries

<ItemsControl>
  <Border Background="#182636" BorderBrush="#2E4A66" BorderThickness="1" Padding="10" Margin="0,0,0,8">
    <TextBlock Text="Audio" />
  </Border>
  <Border Background="#182636" BorderBrush="#2E4A66" BorderThickness="1" Padding="10" Margin="0,0,0,8">
    <TextBlock Text="Video" />
  </Border>
  <Border Background="#182636" BorderBrush="#2E4A66" BorderThickness="1" Padding="10">
    <TextBlock Text="Input" />
  </Border>
</ItemsControl>

Activity feed with typed data

<ItemsControl ItemsSource="{Binding Activity}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <StackPanel Margin="0,0,0,10">
        <TextBlock Text="{Binding Title}" />
        <TextBlock Text="{Binding Timestamp}" Opacity="0.7" />
      </StackPanel>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

Grouped issue list

<UserControl.Resources>
  <CollectionViewSource x:Key="IssuesView">
    <CollectionViewSource.GroupDescriptions>
      <PropertyGroupDescription PropertyName="Severity" />
    </CollectionViewSource.GroupDescriptions>
  </CollectionViewSource>
</UserControl.Resources>

<ItemsControl ItemsSource="{StaticResource IssuesView}">
  <ItemsControl.GroupStyle>
    <GroupStyle HeaderStringFormat="{0}" />
  </ItemsControl.GroupStyle>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Border Background="#182636"
              BorderBrush="#36516B"
              BorderThickness="1"
              Padding="8"
              Margin="0,0,0,6">
        <TextBlock Text="{Binding Title}" />
      </Border>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

Using ItemsPresenter inside a control template

<Style x:Key="FramedItemsControlStyle" TargetType="{x:Type ItemsControl}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type ItemsControl}">
        <Border Background="#101822"
                BorderBrush="#2D465E"
                BorderThickness="1"
                Padding="12">
          <ItemsPresenter />
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

ItemsPresenter becomes the active host for realized item visuals when the control template includes it.

Notes and pitfalls

No built-in selection. ItemsControl repeats items only. If your UI needs selected state, keyboard navigation semantics, or current-item behavior, start from a selector-derived control instead.

No virtualization guarantee. This implementation realizes the active projected item visuals and measures them in order. Large collections should be approached carefully.

ItemContainerStyle is not a universal override. It only applies when the realized container is a FrameworkElement and the control is allowed to set that element's local Style value.

Grouping requires both a grouped view and a GroupStyle. Group descriptions on the view alone are not enough to project grouped containers in this control.

Inline items and ItemsSource do not mix. Treat them as separate modes and pick one per control instance.