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
ItemsSourceis set, modifyingItemsthrows anInvalidOperationException. - If you want to switch back to local items, clear
ItemsSourcefirst. - Do not mix
ItemsSourcewith 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.