Controls

ListBoxItem

ListBoxItem is the selectable row container used by ListBox. It combines the ContentControl content model with selection-aware chrome, hover state, and bubbled selected or unselected routed events so each row can participate cleanly in list selection, templating, and theming.

Quick start

Most ListBoxItem instances are generated automatically by ListBox for plain objects in Items or ItemsSource. You still use the type directly when you want explicit row markup, direct row properties, or per-item content that should stay under your control.

Explicit rows inside a ListBox

<ListBox Width="280"
                 Height="200"
                 SelectedIndex="0">
    <ListBoxItem Padding="10,8">
        <Label Content="Inbox" />
    </ListBoxItem>
    <ListBoxItem Padding="10,8">
        <Label Content="Drafts" />
    </ListBoxItem>
    <ListBoxItem Padding="10,8">
        <Label Content="Archive" />
    </ListBoxItem>
</ListBox>

Generated rows styled through the container

<ListBox Width="320"
                 Height="220"
                 ItemsSource="{Binding Agents}"
                 DisplayMemberPath="Name">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Padding" Value="12,8" />
            <Setter Property="Background" Value="#152230" />
            <Setter Property="SelectedBackground" Value="#28445F" />
            <Setter Property="BorderBrush" Value="#35516B" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

Default usage: put row-level hover, selection, padding, and border visuals on ListBoxItem rather than only styling the content inside it. The row container is the clickable and selectable surface.

Role in the ListBox pipeline

ListBoxItem is the row container contract for ListBox. Whether you create rows explicitly or let the list generate them, selection and hover state live on the container rather than on the inner content.

Explicit container path

If you add ListBoxItem elements directly, the list preserves them. ItemTemplate does not replace those explicit containers.

Generated container path

If the list receives plain objects, it creates ListBoxItem containers and places projected content inside them. That remains true even when ItemTemplate is used.

  • ListBox updates IsSelected as its selection model changes.
  • Pointer hover updates IsMouseOver on the realized row under the pointer.
  • The row content comes from the inherited ContentControl pipeline: direct UIElement content, a selected ContentTemplate, or an implicit generated label for plain values.
  • A ListBoxItem can be authored manually, but it is most useful when hosted by ListBox because that is where selection ownership lives.

Templated content inside an explicit ListBoxItem

<ListBoxItem Padding="12,10">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Border Width="10" Height="10" Background="#4FC7C1" Margin="0,0,10,0" />
        <StackPanel Grid.Column="1">
            <Label Content="Build agent A" />
            <Label Content="Online" Opacity="0.7" />
        </StackPanel>
    </Grid>
</ListBoxItem>

Selection state and routed events

ListBoxItem surfaces row state through dependency properties and also raises bubbled routed events when its selected state changes. The parent list remains responsible for deciding which rows become selected.

State properties

IsSelected tracks whether the row belongs to the list's current selection. IsMouseOver tracks hover state for the row surface.

State events

Selected and Unselected are bubbled routed events raised when IsSelected changes between true and false.

  • The routed events are emitted only when the IsSelected value actually changes.
  • Handlers can be attached either to the row itself or to an ancestor such as the owning ListBox.
  • Changing IsSelected directly affects the row state, but in normal list usage the parent selector should remain the source of truth.

Handle row selection events on the parent list

listBox.AddHandler<RoutedSimpleEventArgs>(ListBoxItem.SelectedEvent, (_, args) =>
{
        if (args.OriginalSource is ListBoxItem item)
        {
                Logger.Write($"Selected row: {item.Content}");
        }
});

listBox.AddHandler<RoutedSimpleEventArgs>(ListBoxItem.UnselectedEvent, (_, args) =>
{
        if (args.OriginalSource is ListBoxItem item)
        {
                Logger.Write($"Unselected row: {item.Content}");
        }
});

Appearance and template behavior

ListBoxItem supports two rendering paths. Without a control template it uses a built-in fallback renderer that fills the row background, switches to SelectedBackground when selected, and draws a one-pixel border. With a template root present, the template owns the visuals and the fallback chrome is skipped.

Fallback renderer

The fallback path uses Background, SelectedBackground, BorderBrush, and Padding directly. It is useful for minimal rows or simple custom styles without a template.

Templated renderer

When a ControlTemplate is applied, the template root becomes responsible for hover, selection, padding, and border visuals. The built-in drawing path no longer paints row chrome.

  • Foreground, Background, and BorderBrush are Color values in this implementation, not brush objects.
  • Padding is a full Thickness and only contributes to layout directly when no template root takes over arrangement.
  • If your template should react to selection, bind or trigger against IsSelected explicitly.
  • If your template should use SelectedBackground, reference it in template bindings or triggers yourself. The default themed template uses IsSelected triggers instead of that property.

Default themed shape in App.xml

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="Padding" Value="10,8" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <Border x:Name="itemBorder"
                                Background="{TemplateBinding Background}"
                                Padding="{TemplateBinding Padding}">
                    <ContentPresenter />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="itemBorder" Property="Background" Value="{StaticResource DarkElevatedBrush}" />
                    </Trigger>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter TargetName="itemBorder" Property="Background" Value="{StaticResource OrangeSubtleBrush}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Custom template with explicit selected-state visuals

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <Border x:Name="itemBorder"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                Padding="{TemplateBinding Padding}">
                    <ContentPresenter />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter TargetName="itemBorder" Property="Background" Value="#28445F" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Layout and content model

Because ListBoxItem inherits from ContentControl, it can host a direct element, a templated content projection, or a simple generated label for plain values. The container then adds row-specific chrome and layout behavior around that content.

Untemplated layout

Without a template root, the item measures its content and then adds Padding around it. During arrange, the content is inset by that padding.

Templated layout

With a template root, layout is delegated to the template tree. Your template decides where the ContentPresenter goes and how padding is applied.

  • Direct UIElement content is hosted as the row's content element.
  • ContentTemplate and ContentTemplateSelector can project plain values into richer visuals.
  • When no template or direct element is present, plain content falls back to a generated label using ToString().

Templated content projection on a single row

<ListBoxItem Content="Alpha" Padding="12,8">
    <ListBoxItem.ContentTemplate>
        <DataTemplate>
            <StackPanel>
                <Label Content="{Binding}" />
                <Label Content="Secondary line" Opacity="0.7" />
            </StackPanel>
        </DataTemplate>
    </ListBoxItem.ContentTemplate>
</ListBoxItem>

Property reference

Selection and interaction

IsSelected: whether the row is currently selected.

IsMouseOver: whether the pointer is currently over the row surface.

Selected and Unselected: bubbled routed events raised when selected state changes.

Row chrome

Background: base row fill color for fallback or template-bound visuals.

SelectedBackground: selected-state fill color used by the fallback renderer.

BorderBrush: row border color.

Foreground: foreground color inherited by row content that respects it.

Padding: inner spacing around the row content.

Inherited content API

Content: the row's content payload.

ContentTemplate and ContentTemplateSelector: template-based content projection for the row body.

Template behavior

Template: custom control template for replacing the built-in row visuals.

When a template root exists, fallback drawing and padding-based arrange logic are bypassed in favor of the template tree.

Patterns and examples

Inline navigation row

<ListBoxItem Padding="12,8">
    <Label Content="Diagnostics" />
</ListBoxItem>

Row with content template

<ListBoxItem Content="Build agent A" Padding="12,8">
    <ListBoxItem.ContentTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Border Width="10" Height="10" Background="#4FC7C1" Margin="0,0,10,0" />
                <Label Grid.Column="1" Content="{Binding}" />
            </Grid>
        </DataTemplate>
    </ListBoxItem.ContentTemplate>
</ListBoxItem>

Inspect selection transitions

item.Selected += (_, _) => Logger.Write("Row selected");
item.Unselected += (_, _) => Logger.Write("Row unselected");

Notes and pitfalls

Selection ownership belongs to the parent list. A ListBoxItem exposes IsSelected, but in normal usage ListBox should control that state so the selection model stays coherent.

Templates replace fallback chrome. Once a template root exists, Background, SelectedBackground, BorderBrush, and Padding only matter insofar as your template reads or reacts to them.

Use container styling for row visuals. If you style only the inner content, hover and selected visuals can look disconnected from the clickable row surface.

Color semantics are not brush semantics. The item's foreground and background properties are Color values in this implementation, so template and style examples should use color-compatible values.