Wpf – Change DataTemplate style in ListBoxItem if the item is selected

listboxstylestriggerswpfxaml

I have a listbox with an expander inside the ItemTemplate. I managed to bind the expander's IsExpanded property to the ListBoxItem's IsSelected property ok. Now I want to apply a style to the ListBoxItem's content also bound to the IsSelected property.

    <ListBox.ItemTemplate>
        <DataTemplate>
            <Border Name="myBorder">
                    <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding Description}" />
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="Date:"/>
                        <TextBlock Text="{Binding Date}"/>
                    </StackPanel>
                    <dx:DXExpander Name="expanderDetails" 
                              IsExpanded="{Binding Mode=TwoWay, Path=IsSelected,
                              RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Count:"/>
                            <TextBlock Text="{Binding Count}"/>
                        </StackPanel>
                    </dx:DXExpander>
                </StackPanel>
            </Border>
        </DataTemplate>
    </ListBox.ItemTemplate>

What I want to do is somehow set the style of the "myBorder" Border to "NotSelectedBorderStyle" for unselected ListBoxItems, and to "SelectedBorderStyle" for the SelectedItem (ListBox with single selection).

FYI, the styles define background, border and that kind of stuff, just to make the clear which item is selected, nothing fancy in these styles.

I tried the accepted answer here but if I completely switch styles I loose the nice expanding animation my DXExpander has.

I guess there must be some solution using triggers, but I can't just hit the right spot.

Best Answer

Finally I got it, I'm posting it here in the hope that this will save someone else time and pain :-P

This code does some additional things: the EventSetter and the corresponding Handler method are there to capture clicks to the elements inside the DataTemplate, in order to select the ListBoxItem which contains the element (if you don't you might type text inside an item, while a different one is selected).

The inner Border ("myBorder") is just a container for the stackpanels, I had to wrap everything inside another border ("backgroundBorder") which gets the style changed when the ListBoxItem gets selected.

    <Style x:Key="FocusedContainer" TargetType="{x:Type ListBoxItem}">
        <Setter Property="Background" Value="LightGray"/>
        <EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Border x:Name="backgroundBorder" Width="Auto" Style="{StaticResource NotSelectedBorderStyle}">
                        <ContentPresenter Content="{TemplateBinding Content}">
                            <ContentPresenter.ContentTemplate>
                                <DataTemplate>
                                    <Border Name="myBorder">
                                         <StackPanel Orientation="Vertical">
                                               <TextBlock Text="{Binding Description}" />
                                         <StackPanel Orientation="Horizontal">
                                               <TextBlock Text="Date:"/>
                                               <TextBlock Text="{Binding Date}"/>
                                         </StackPanel>
                                         <dx:DXExpander Name="expanderDetails" 
                                             IsExpanded="{Binding Mode=TwoWay, Path=IsSelected,
                                             RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}}">
                                                <StackPanel Orientation="Horizontal">
                                                     <TextBlock Text="Count:"/>
                                                     <TextBlock Text="{Binding Count}"/>
                                                </StackPanel>
                                         </dx:DXExpander>
                                       </StackPanel>
                                    </Border>
                                </DataTemplate>
                            </ContentPresenter.ContentTemplate>
                        </ContentPresenter>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="backgroundBorder" Property="Style" Value="{StaticResource SelectedBorderStyle}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Then I set the ItemContainerStyle in my ListBox to the above style:

<ListBox Background="#7FFFFFFF" HorizontalContentAlignment="Stretch" 
         ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True"
         ItemContainerStyle="{StaticResource FocusedContainer}"/>

To finish, the code behind for the GotKeyBoardFocus handler:

    private void OnListBoxItemContainerFocused(object sender, RoutedEventArgs e)
    {
        (sender as ListBoxItem).IsSelected = true;
    }

A mess in code, but pretty neat in the UI. Hope this helps someone!

Related Topic