Alterar DataTemplate WPF para o item ListBox se selecionado

89

Eu preciso mudar o DataTemplate para itens em uma ListBox dependendo se o item está selecionado ou não (exibindo informações diferentes / mais quando selecionado).

Eu não recebo um evento GotFocus / LostFocus no elemento superior no DataTemplate (um StackPanel) ao clicar no item ListBox em questão (apenas por meio de tabulação), e estou sem ideias.

Daniel Beck
fonte

Respostas:

182

A maneira mais fácil de fazer isso é fornecer um modelo para o "ItemContainerStyle" e NÃO a propriedade "ItemTemplate". No código abaixo, crio 2 modelos de dados: um para os estados "não selecionados" e um para os estados "selecionados". Em seguida, crio um modelo para o "ItemContainerStyle" que é o "ListBoxItem" real que contém o item. Eu defino o "ContentTemplate" padrão para o estado "Unselected" e, em seguida, forneço um gatilho que troca o modelo quando a propriedade "IsSelected" é verdadeira. (Observação: estou definindo a propriedade "ItemsSource" no código por trás de uma lista de strings 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}" />
Micah
fonte
Obrigado, inclua <ListBox ItemContainerStyle = ”{StaticResource ContainerStyle}” ItemsSource = ”{Binding MyData}” /> em sua postagem, para que as pessoas não precisem pesquisar em seu blog.
Shimmy Weitzhandler,
2
Um problema que encontrei ao definir o ContainerStyle do ListBox é que ele causa incompatibilidade com temas. Usei sua abordagem, mas quando apliquei o tema a do conjunto WPF Futures, o ListBoxItems tinha o estilo padrão em vez do estilo do tema. No meu caso, texto preto sobre fundo preto e feiura geral. Ainda estou procurando outra abordagem, talvez usando gatilhos DataTemplate.
Benny Jobigan
1
Além disso, se você quiser que seu novo ItemContainerStyle seja compatível com temas, você deve basear-se naquele do tema. Para fazer isso, use BasedOn="{StaticResource {x:Type ListBoxItem}}"com ListBox. Isso também se aplica a outros controles como TreeView.
Benny Jobigan
5
Ao usar isso, descobri que precisava declarar os DataTemplates acima do Estilo na seção Recursos para não obter erros XAML misteriosos. Apenas um aviso sobre isso.
Rob Perkins,
8

Para definir o estilo quando o item é selecionado ou não, tudo o que você precisa fazer é recuperar o ListBoxItempai em seu <DataTemplate>e acionar as mudanças de estilo quando ele IsSelectedmudar. Por exemplo, o código abaixo criará um TextBlockcom a Foregroundcor padrão verde . Agora, se o item for selecionado, a fonte ficará vermelha e quando o mouse estiver sobre o item ficará amarelo . Dessa forma, você não precisa especificar modelos de dados separados, conforme sugerido em outras respostas, para cada estado que deseja alterar ligeiramente.

<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
fonte
1
Como escrevi na pergunta, na verdade estou mostrando mais informações se selecionado (" exibindo diferentes / mais informações quando selecionado "). Ainda assim, se isso pudesse funcionar com visibilidade alternada de alguns elementos (incluindo se eles ocupam tamanho), essa seria uma solução viável. Não trabalho com WPF há algum tempo.
Daniel Beck
6

Também deve ser observado que o painel de pilha não pode ser focalizado, então ele nunca receberá o foco (defina Focusable = True se você / realmente / quiser focalizá-lo). No entanto, a chave a lembrar em cenários como este é que o Stackpanel é filho do TreeViewItem, que é o ItemContainer neste caso. Como Micah sugere, ajustar o estilo do item-recipiente é uma boa abordagem.

Você provavelmente poderia fazer isso usando DataTemplates e coisas como datatriggers que usariam a extensão de marcação RelativeSouce para procurar pelo item de exibição de lista

Dominic Hopton
fonte