Modificare il DataTemplate WPF per l'elemento ListBox, se selezionato


89

Ho bisogno di cambiare il DataTemplate per gli elementi in un ListBox a seconda che l'elemento sia selezionato o meno (visualizzando diverse / più informazioni quando selezionato).

Non ottengo un evento GotFocus / LostFocus sull'elemento più in alto nel DataTemplate (uno StackPanel) quando faccio clic sull'elemento ListBox in questione (solo tramite tabulazione) e sono a corto di idee.

Risposte:


182

Il modo più semplice per eseguire questa operazione è fornire un modello per "ItemContainerStyle" e NON per la proprietà "ItemTemplate". Nel codice sottostante creo 2 modelli di dati: uno per gli stati "non selezionato" e uno per gli stati "selezionato". Quindi creo un modello per "ItemContainerStyle" che è l'effettivo "ListBoxItem" che contiene l'elemento. Ho impostato il "ContentTemplate" predefinito sullo stato "Unselected", quindi fornisco un trigger che sostituisce il modello quando la proprietà "IsSelected" è vera. (Nota: sto impostando la proprietà "ItemsSource" nel codice sottostante su un elenco di stringhe per semplicità)

<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}" />

Grazie, includi <ListBox ItemContainerStyle = "{StaticResource ContainerStyle}" ItemsSource = "{Binding MyData}" /> nel tuo post, in modo che le persone non debbano cercare nel tuo blog.
Shimmy Weitzhandler

2
Un problema che ho riscontrato con l'impostazione del ContainerStyle di ListBox è che causa incompatibilità con i temi. Ho usato il tuo approccio, ma quando ho applicato il tema a dal set WPF Futures, ListBoxItems aveva lo stile predefinito invece dello stile del tema. Nel mio caso, testo nero su sfondo nero e bruttezza generale. Sto ancora cercando un altro approccio, magari utilizzando i trigger DataTemplate.
Benny Jobigan

1
Inoltre, se vuoi che il tuo nuovo ItemContainerStyle sia compatibile con i temi, devi basarlo su quello del tema. A tale scopo, utilizzare BasedOn="{StaticResource {x:Type ListBoxItem}}"con ListBox. Questo vale anche per altri controlli come TreeView.
Benny Jobigan

5
Utilizzando questo ho scoperto che dovevo dichiarare i DataTemplates sopra lo stile nella sezione Risorse per non ottenere errori XAML arcani. Solo un avvertimento a riguardo.
Rob Perkins

8

Per impostare lo stile quando l'elemento è selezionato o meno, tutto ciò che devi fare è recuperare il ListBoxItemgenitore nel tuo <DataTemplate>e attivare le modifiche di stile quando IsSelectedcambia. Ad esempio, il codice seguente creerà un TextBlockcon il Foregroundcolore predefinito verde . Ora se l'elemento viene selezionato, il carattere diventerà rosso e quando il mouse è sopra l'elemento diventerà giallo . In questo modo non è necessario specificare modelli di dati separati come suggerito in altre risposte per ogni stato che si desidera modificare leggermente.

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

1
Come ho scritto nella domanda, in realtà sto mostrando più informazioni se selezionato (" visualizzazione di informazioni diverse / più quando selezionato "). Tuttavia, se questo potesse essere fatto per funzionare con l'attivazione / disattivazione della visibilità di alcuni elementi (incluso se assumono dimensioni), questa sarebbe una soluzione praticabile. Non ho lavorato con WPF per un po 'però.
Daniel Beck

6

Va anche notato che lo stackpanel non è focalizzabile, quindi non verrà mai messo a fuoco (imposta Focusable = True se / davvero / lo vuoi focalizzato). Tuttavia, la chiave da ricordare in scenari come questo è che Stackpanel è figlio di TreeViewItem, che in questo caso è ItemContainer. Come suggerisce Michea, modificare lo stile contenitore è un buon approccio.

Probabilmente potresti farlo usando DataTemplates e cose come i datatriggers che userebbero l'estensione di markup RelativeSouce per cercare l'elemento listview

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.