93

I need to change the DataTemplate for items in a ListBox depending on whether the item is selected or not (displaying different/more information when selected).

I don't get a GotFocus/LostFocus event on the top-most element in the DataTemplate (a StackPanel) when clicking the ListBox item in question (only through tabbing), and I'm out of ideas.

Vertexwahn
  • 7,709
  • 6
  • 64
  • 90
Daniel Beck
  • 6,363
  • 3
  • 35
  • 42

3 Answers3

187

The easiest way to do this is to supply a template for the "ItemContainerStyle" and NOT the "ItemTemplate" property. In the code below I create 2 data templates: one for the "unselected" and one for the "selected" states. I then create a template for the "ItemContainerStyle" which is the actual "ListBoxItem" that contains the item. I set the default "ContentTemplate" to the "Unselected" state, and then supply a trigger that swaps out the template when the "IsSelected" property is true. (Note: I am setting the "ItemsSource" property in the code behind to a list of strings for simplicity)

<Window.Resources>

<DataTemplate x:Key="ItemTemplate">
    <TextBlock Text="{Binding}" Foreground="Red" />
</DataTemplate>

<DataTemplate x:Key="SelectedTemplate">
    <TextBlock Text="{Binding}" Foreground="White" />
</DataTemplate>

<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
    <Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
    <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="ContentTemplate" Value="{StaticResource SelectedTemplate}" />
        </Trigger>
    </Style.Triggers>
</Style>

</Window.Resources>
<ListBox x:Name="lstItems" ItemContainerStyle="{StaticResource ContainerStyle}" />
Micah
  • 111,873
  • 86
  • 233
  • 325
  • 1
    Thank you, please include the in your post, so people don't have to search in your blog. – Shimmy Weitzhandler Sep 09 '09 at 02:48
  • 2
    One problem I encountered with setting the ListBox's ContainerStyle is that it causes incompatibility with themes. I used your approach, but when I applied the a theme from the WPF Futures set the ListBoxItems had the default styling instead of the theme styling. In my case, black text on black background and general ugliness. I'm still searching for another approach, perhaps using DataTemplate triggers. – Benny Jobigan Feb 01 '10 at 03:13
  • 1
    Also, if you want your new ItemContainerStyle to be compatible with themes, you have to base it on the one from the theme. To do this, use `BasedOn="{StaticResource {x:Type ListBoxItem}}"` with ListBox. This also applies to other controls like TreeView. – Benny Jobigan Feb 02 '10 at 03:19
  • 5
    In using this I found I had to declare the DataTemplates above the Style in the Resources section in order to not get arcane XAML errors. Just a heads-up about that. – Rob Perkins Oct 03 '12 at 07:09
14

To set the style when the item is selected or not all you need to do is to retrieve the ListBoxItem parent in your <DataTemplate> and trigger style changes when its IsSelected changes. For example the code below will create a TextBlock with default Foreground color green. Now if the item gets selected the font will turn red and when the mouse is over the item will turn yellow. That way you don't need to specify separate data templates as suggested in other answers for every state you'd like to slightly change.

<DataTemplate x:Key="SimpleDataTemplate">
    <TextBlock Text="{Binding}">
        <TextBlock.Style>
            <Style>
                <Setter Property="TextBlock.Foreground" Value="Green"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={
                        RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem }}}"
                                 Value="True">
                        <Setter Property="TextBlock.Foreground" Value="Red"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={
                        RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem }}}"
                                 Value="True">
                        <Setter Property="TextBlock.Foreground" Value="Yellow"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</DataTemplate>
Darien Pardinas
  • 5,910
  • 1
  • 41
  • 48
  • 1
    As I wrote in the question, I'm actually showing *more information* if selected ("*displaying different/more information when selected*"). Still, if this could be made to work with toggling visibility of some elements (including whether they take up size), this would be a viable solution. Haven't worked with WPF in a while though. – Daniel Beck Dec 23 '14 at 12:39
6

It should also be noted, that the stackpanel isn't focuable, so it's never going to get focus (set Focusable=True if you /really/ want it focused). However, the key to remember in scenarios like this is that the Stackpanel is child of the TreeViewItem, which is the ItemContainer in this case. As Micah suggests, tweaking the itemcontainerstyle is a good approach.

You could probably do it using DataTemplates, and things such as datatriggers which would use the RelativeSouce markup extension to look for the listviewitem

Dominic Hopton
  • 7,262
  • 1
  • 22
  • 29