WPF
DataTemplate
ListBox
SelectedItem
UI Development

Change WPF DataTemplate for ListBox item if selected

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In WPF, you can change the DataTemplate of a ListBox item when it is selected by using a DataTemplateSelector, a Style with DataTrigger, or visual state triggers. The most common approach is creating a custom DataTemplateSelector that returns different templates based on the item's selection state. This allows unselected items to show a compact view and selected items to show an expanded view with additional details or controls.

Using DataTemplateSelector

Define two templates and a selector that picks between them:

xml
1<Window.Resources>
2    <!-- Compact template for unselected items -->
3    <DataTemplate x:Key="NormalTemplate">
4        <TextBlock Text="{Binding Name}" FontSize="14" Margin="5"/>
5    </DataTemplate>
6
7    <!-- Expanded template for selected items -->
8    <DataTemplate x:Key="SelectedTemplate">
9        <StackPanel Margin="5">
10            <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"/>
11            <TextBlock Text="{Binding Description}" FontSize="12"
12                       Foreground="Gray" TextWrapping="Wrap"/>
13            <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
14                <Button Content="Edit" Margin="0,0,5,0"/>
15                <Button Content="Delete"/>
16            </StackPanel>
17        </StackPanel>
18    </DataTemplate>
19</Window.Resources>
20
21<ListBox ItemsSource="{Binding Items}"
22         ItemTemplateSelector="{StaticResource TemplateSelector}"/>

The selector class:

csharp
1public class ListBoxItemTemplateSelector : DataTemplateSelector
2{
3    public DataTemplate NormalTemplate { get; set; }
4    public DataTemplate SelectedTemplate { get; set; }
5
6    public override DataTemplate SelectTemplate(object item, DependencyObject container)
7    {
8        if (container is FrameworkElement element)
9        {
10            var listBoxItem = FindParent<ListBoxItem>(element);
11            if (listBoxItem != null && listBoxItem.IsSelected)
12            {
13                return SelectedTemplate;
14            }
15        }
16        return NormalTemplate;
17    }
18
19    private static T FindParent<T>(DependencyObject child) where T : DependencyObject
20    {
21        var parent = VisualTreeHelper.GetParent(child);
22        while (parent != null && parent is not T)
23        {
24            parent = VisualTreeHelper.GetParent(parent);
25        }
26        return parent as T;
27    }
28}

Register the selector in XAML:

xml
1<Window.Resources>
2    <local:ListBoxItemTemplateSelector
3        x:Key="TemplateSelector"
4        NormalTemplate="{StaticResource NormalTemplate}"
5        SelectedTemplate="{StaticResource SelectedTemplate}"/>
6</Window.Resources>

Using Style Triggers (Simpler Approach)

Use ContentTemplate triggers on ListBoxItem style:

xml
1<Window.Resources>
2    <DataTemplate x:Key="NormalTemplate">
3        <TextBlock Text="{Binding Name}" FontSize="14"/>
4    </DataTemplate>
5
6    <DataTemplate x:Key="SelectedTemplate">
7        <StackPanel>
8            <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"/>
9            <TextBlock Text="{Binding Description}" Foreground="Gray"/>
10        </StackPanel>
11    </DataTemplate>
12
13    <Style TargetType="ListBoxItem">
14        <Setter Property="ContentTemplate" Value="{StaticResource NormalTemplate}"/>
15        <Style.Triggers>
16            <Trigger Property="IsSelected" Value="True">
17                <Setter Property="ContentTemplate"
18                        Value="{StaticResource SelectedTemplate}"/>
19            </Trigger>
20        </Style.Triggers>
21    </Style>
22</Window.Resources>
23
24<ListBox ItemsSource="{Binding Items}"/>

This approach is simpler — no C# code needed. The ListBoxItem style swaps the ContentTemplate when IsSelected changes.

Using DataTrigger with ViewModel Property

Bind selection state to a ViewModel property:

csharp
1public class ItemViewModel : INotifyPropertyChanged
2{
3    public string Name { get; set; }
4    public string Description { get; set; }
5
6    private bool _isSelected;
7    public bool IsSelected
8    {
9        get => _isSelected;
10        set { _isSelected = value; OnPropertyChanged(); }
11    }
12
13    public event PropertyChangedEventHandler PropertyChanged;
14    protected void OnPropertyChanged([CallerMemberName] string name = null)
15        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
16}
xml
1<ListBox ItemsSource="{Binding Items}">
2    <ListBox.ItemContainerStyle>
3        <Style TargetType="ListBoxItem">
4            <Setter Property="IsSelected"
5                    Value="{Binding IsSelected, Mode=TwoWay}"/>
6        </Style>
7    </ListBox.ItemContainerStyle>
8
9    <ListBox.ItemTemplate>
10        <DataTemplate>
11            <StackPanel>
12                <TextBlock Text="{Binding Name}" FontSize="14"/>
13                <!-- Only visible when selected -->
14                <StackPanel Visibility="{Binding IsSelected,
15                    Converter={StaticResource BoolToVisibility}}">
16                    <TextBlock Text="{Binding Description}" Foreground="Gray"/>
17                    <Button Content="Details"/>
18                </StackPanel>
19            </StackPanel>
20        </DataTemplate>
21    </ListBox.ItemTemplate>
22</ListBox>

This keeps a single template but toggles element visibility based on selection.

Animating the Transition

xml
1<Style TargetType="ListBoxItem">
2    <Setter Property="ContentTemplate" Value="{StaticResource NormalTemplate}"/>
3    <Style.Triggers>
4        <Trigger Property="IsSelected" Value="True">
5            <Trigger.EnterActions>
6                <BeginStoryboard>
7                    <Storyboard>
8                        <DoubleAnimation Storyboard.TargetProperty="Height"
9                                         From="40" To="120" Duration="0:0:0.2"/>
10                    </Storyboard>
11                </BeginStoryboard>
12            </Trigger.EnterActions>
13            <Setter Property="ContentTemplate"
14                    Value="{StaticResource SelectedTemplate}"/>
15        </Trigger>
16    </Style.Triggers>
17</Style>

Common Pitfalls

  • DataTemplateSelector not updating on selection change: DataTemplateSelector.SelectTemplate is called when the item is first loaded, not when selection changes. Force a re-evaluation by setting ItemTemplateSelector to null and back, or use the Style trigger approach which responds to property changes automatically.
  • Forgetting to walk the visual tree in the selector: The container parameter in SelectTemplate is the ContentPresenter, not the ListBoxItem. You must walk up the visual tree with VisualTreeHelper.GetParent to find the ListBoxItem and check IsSelected.
  • Using ItemTemplate and ItemTemplateSelector together: Setting both ItemTemplate and ItemTemplateSelector on the same ListBox causes ItemTemplate to take precedence, ignoring the selector. Use one or the other, not both.
  • Not binding IsSelected two-way on the container style: When using the ViewModel IsSelected pattern, the binding must be Mode=TwoWay so that clicking a ListBoxItem updates the ViewModel property. Without TwoWay, the ViewModel property never changes.
  • Performance with complex selected templates: If the selected template contains heavy controls (images, grids, data-bound lists), rapidly switching selection causes layout thrashing. Use virtualization (VirtualizingStackPanel.IsVirtualizing="True") and keep selected templates lightweight.

Summary

  • Use Style.Triggers on ListBoxItem to swap ContentTemplate based on IsSelected — simplest approach
  • Use DataTemplateSelector for more complex logic (e.g., different templates based on data type and selection)
  • Use Visibility binding with BoolToVisibilityConverter when only parts of the template should change
  • Bind ListBoxItem.IsSelected to a ViewModel property with Mode=TwoWay for MVVM-friendly selection tracking
  • DataTemplateSelector is not re-evaluated on selection change — use triggers for dynamic switching
  • Keep selected templates lightweight to avoid layout performance issues

Course illustration
Course illustration

All Rights Reserved.