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:
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:
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:
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:
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:
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}
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
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