Cambiar WPF DataTemplate para el elemento ListBox si está seleccionado

91

Necesito cambiar el DataTemplate para elementos en un ListBox dependiendo de si el elemento está seleccionado o no (mostrando diferente / más información cuando se selecciona).

No obtengo un evento GotFocus / LostFocus en el elemento superior en DataTemplate (un StackPanel) al hacer clic en el elemento ListBox en cuestión (solo a través de tabulaciones), y no tengo ideas.

Daniel Beck
fuente

Respuestas:

184

La forma más sencilla de hacer esto es proporcionar una plantilla para el "ItemContainerStyle" y NO la propiedad "ItemTemplate". En el siguiente código, creo 2 plantillas de datos: una para los estados "no seleccionados" y otra para los estados "seleccionados". Luego creo una plantilla para el "ItemContainerStyle" que es el "ListBoxItem" real que contiene el elemento. Establezco el "ContentTemplate" predeterminado en el estado "No seleccionado" y luego proporciono un activador que cambia la plantilla cuando la propiedad "IsSelected" es verdadera. (Nota: estoy configurando la propiedad "ItemsSource" en el código subyacente a una lista de cadenas para simplificar)

<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}" />
Miqueas
fuente
1
Gracias, incluya <ListBox ItemContainerStyle = ”{StaticResource ContainerStyle}” ItemsSource = ”{Binding MyData}” /> en su publicación, para que la gente no tenga que buscar en su blog.
Shimmy Weitzhandler
2
Un problema que encontré al configurar el estilo de contenedor de ListBox es que causa incompatibilidad con los temas. Usé su enfoque, pero cuando apliqué el tema a del conjunto WPF Futures, ListBoxItems tenía el estilo predeterminado en lugar del estilo del tema. En mi caso, texto negro sobre fondo negro y fealdad generalizada. Todavía estoy buscando otro enfoque, tal vez usando desencadenadores DataTemplate.
Benny Jobigan
1
Además, si desea que su nuevo ItemContainerStyle sea compatible con los temas, debe basarlo en el del tema. Para hacer esto, use BasedOn="{StaticResource {x:Type ListBoxItem}}"con ListBox. Esto también se aplica a otros controles como TreeView.
Benny Jobigan
5
Al usar esto, descubrí que tenía que declarar las plantillas de datos sobre el estilo en la sección Recursos para no obtener errores arcanos de XAML. Solo un aviso sobre eso.
Rob Perkins
8

Para establecer el estilo cuando el elemento está seleccionado o no, todo lo que necesita hacer es recuperar el ListBoxItempadre en su <DataTemplate>y activar cambios de estilo cuando IsSelectedcambie. Por ejemplo, el siguiente código creará un color verdeTextBlock predeterminado . Ahora, si el elemento se selecciona, la fuente se volverá roja y cuando el mouse esté sobre el elemento se volverá amarillo . De esa manera, no es necesario que especifique plantillas de datos separadas como se sugiere en otras respuestas para cada estado que le gustaría cambiar ligeramente.Foreground

<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
fuente
1
Como escribí en la pregunta, en realidad estoy mostrando más información si se selecciona (" mostrar información diferente / más cuando se selecciona "). Aún así, si se pudiera hacer que esto funcionara alternando la visibilidad de algunos elementos (incluido si ocupan el tamaño), esta sería una solución viable. Sin embargo, no he trabajado con WPF por un tiempo.
Daniel Beck
6

También debe tenerse en cuenta que el panel de pila no se puede enfocar, por lo que nunca se enfocará (establezca Focusable = True si usted / realmente / quiere que esté enfocado). Sin embargo, la clave para recordar en escenarios como este es que el Stackpanel es hijo del TreeViewItem, que es el ItemContainer en este caso. Como sugiere Micah, modificar el estilo del contenedor de elementos es un buen enfoque.

Probablemente podría hacerlo usando DataTemplates y cosas como datatriggers que usarían la extensión de marcado RelativeSouce para buscar el elemento listview

Dominic Hopton
fuente