Modifier WPF DataTemplate pour l'élément ListBox s'il est sélectionné

89

J'ai besoin de changer le DataTemplate pour les éléments dans un ListBox selon que l'élément est sélectionné ou non (affichage d'informations différentes / plus lorsqu'il est sélectionné).

Je n'obtiens pas d'événement GotFocus / LostFocus sur l'élément le plus haut du DataTemplate (un StackPanel) lorsque je clique sur l'élément ListBox en question (uniquement par tabulation), et je suis à court d'idées.

Daniel Beck
la source

Réponses:

182

Le moyen le plus simple de procéder consiste à fournir un modèle pour la propriété "ItemContainerStyle" et NON pour la propriété "ItemTemplate". Dans le code ci-dessous, je crée 2 modèles de données: un pour les états "non sélectionnés" et un pour les états "sélectionnés". Je crée ensuite un modèle pour le "ItemContainerStyle" qui est le "ListBoxItem" réel qui contient l'élément. J'ai mis le "ContentTemplate" par défaut à l'état "Unselected", puis je fournis un déclencheur qui permute le modèle lorsque la propriété "IsSelected" est vraie. (Remarque: je suis en train de définir la propriété "ItemsSource" dans le code derrière une liste de chaînes pour plus de simplicité)

<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}" />
Michée
la source
Merci, veuillez inclure le <ListBox ItemContainerStyle = ”{StaticResource ContainerStyle}” ItemsSource = ”{Binding MyData}” /> dans votre message, afin que les gens n'aient pas à chercher dans votre blog.
Shimmy Weitzhandler
2
Un problème que j'ai rencontré lors de la définition du ContainerStyle de ListBox est qu'il provoque une incompatibilité avec les thèmes. J'ai utilisé votre approche, mais lorsque j'ai appliqué un thème du jeu WPF Futures, ListBoxItems avait le style par défaut au lieu du style du thème. Dans mon cas, texte noir sur fond noir et laideur générale. Je recherche toujours une autre approche, peut-être en utilisant des déclencheurs DataTemplate.
Benny Jobigan
1
De plus, si vous voulez que votre nouveau ItemContainerStyle soit compatible avec les thèmes, vous devez le baser sur celui du thème. Pour ce faire, utilisez BasedOn="{StaticResource {x:Type ListBoxItem}}"avec ListBox. Cela s'applique également à d'autres contrôles tels que TreeView.
Benny Jobigan
5
En utilisant cela, j'ai découvert que je devais déclarer les DataTemplates au-dessus du style dans la section Resources afin de ne pas obtenir d'erreurs XAML arcanes. Juste un avertissement à ce sujet.
Rob Perkins
8

Pour définir le style lorsque l'élément est sélectionné ou pas, tout ce que vous avez à faire est de récupérer le ListBoxItemparent dans votre <DataTemplate>et de déclencher les changements de style lorsque celui-ci IsSelectedchange. Par exemple, le code ci-dessous créera un TextBlockavec la Foregroundcouleur par défaut verte . Maintenant, si l'élément est sélectionné, la police deviendra rouge et lorsque la souris sera sur l'élément deviendra jaune . De cette façon, vous n'avez pas besoin de spécifier des modèles de données séparés comme suggéré dans d'autres réponses pour chaque état que vous souhaitez modifier légèrement.

<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
la source
1
Comme je l'ai écrit dans la question, je montre en fait plus d'informations si sélectionné (" afficher des informations différentes / plus lorsque sélectionné "). Pourtant, si cela pouvait être fait pour fonctionner avec une visibilité changeante de certains éléments (y compris s'ils prennent de la taille), ce serait une solution viable. Je n'ai pas travaillé avec WPF depuis un moment.
Daniel Beck
6

Il convient également de noter que le stackpanel n'est pas focalisable, donc il ne le sera jamais (définissez Focusable = True si vous / vraiment / voulez qu'il soit concentré). Cependant, la clé à retenir dans des scénarios comme celui-ci est que le Stackpanel est l' enfant de TreeViewItem, qui est le ItemContainer dans ce cas. Comme le suggère Micah, peaufiner le style du conteneur est une bonne approche.

Vous pouvez probablement le faire à l'aide de DataTemplates et de choses telles que des déclencheurs de données qui utiliseraient l'extension de balisage RelativeSouce pour rechercher l'élément listview

Dominic Hopton
la source