Zmień WPF DataTemplate dla elementu ListBox, jeśli wybrano


91

Muszę zmienić DataTemplate dla elementów w ListBox w zależności od tego, czy element jest zaznaczony, czy nie (wyświetlanie różnych / więcej informacji po wybraniu).

Nie dostaję zdarzenia GotFocus / LostFocus na najwyższym elemencie w DataTemplate (StackPanel) po kliknięciu danego elementu ListBox (tylko poprzez tabulację) i nie mam pomysłów.

Odpowiedzi:


184

Najłatwiejszym sposobem jest dostarczenie szablonu dla właściwości „ItemContainerStyle”, a NIE właściwości „ItemTemplate”. W poniższym kodzie tworzę 2 szablony danych: jeden dla stanów „niewybranych” i jeden dla stanów „zaznaczonych”. Następnie tworzę szablon dla „ItemContainerStyle”, który jest rzeczywistym „ListBoxItem” zawierającym element. Ustawiam domyślny „ContentTemplate” na stan „Unselected”, a następnie podaję wyzwalacz, który zamienia szablon, gdy właściwość „IsSelected” ma wartość true. (Uwaga: ustawiam właściwość „ItemsSource” w kodzie za listą ciągów dla uproszczenia)

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

1
Dziękujemy, dołącz <ListBox ItemContainerStyle = ”{StaticResource ContainerStyle}” ItemsSource = ”{Binding MyData}” /> w swoim poście, aby ludzie nie musieli przeszukiwać Twojego bloga.
Shimmy Weitzhandler

2
Jednym z problemów, które napotkałem podczas ustawiania ContainerStyle ListBox, jest to, że powoduje niekompatybilność z motywami. Użyłem twojego podejścia, ale kiedy zastosowałem motyw z zestawu WPF Futures, ListBoxItems miał domyślny styl zamiast stylu motywu. W moim przypadku czarny tekst na czarnym tle i ogólna brzydota. Nadal szukam innego podejścia, być może przy użyciu wyzwalaczy DataTemplate.
Benny Jobigan

1
Ponadto, jeśli chcesz, aby nowy ItemContainerStyle był zgodny z motywami, musisz oprzeć go na motywie z motywu. Aby to zrobić, użyj BasedOn="{StaticResource {x:Type ListBoxItem}}"z ListBox. Dotyczy to również innych kontrolek, takich jak TreeView.
Benny Jobigan

5
Używając tego, stwierdziłem, że muszę zadeklarować DataTemplates powyżej Styl w sekcji Zasoby, aby nie uzyskać tajemniczych błędów XAML. Tylko ostrzeżenie o tym.
Rob Perkins

8

Aby ustawić styl, gdy element jest zaznaczony lub nie wszystko, co musisz zrobić, to odzyskać ListBoxItemrodzica w swoim <DataTemplate>i wywołać zmiany stylu, gdy się IsSelectedzmieni. Na przykład poniższy kod utworzy TextBlockdomyślny Foregroundkolor zielony . Teraz, jeśli element zostanie wybrany, czcionka zmieni kolor na czerwony, a gdy wskaźnik myszy znajdzie się nad elementem, zmieni kolor na żółty . W ten sposób nie musisz określać oddzielnych szablonów danych, jak sugerują inne odpowiedzi dla każdego stanu, który chcesz nieznacznie zmienić.

<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
Jak napisałem w pytaniu, w rzeczywistości wyświetlam więcej informacji, jeśli wybrano („ wyświetlam inne / więcej informacji po wybraniu ”). Mimo to, gdyby można było to zmusić do przełączania widoczności niektórych elementów (w tym tego, czy przyjmują rozmiar), byłoby to realne rozwiązanie. Jednak od jakiegoś czasu nie współpracowałem z WPF.
Daniel Beck

6

Należy również zauważyć, że panel stosu nie jest focu-up, więc nigdy nie zostanie skupiony (ustaw Focusable = True, jeśli chcesz / naprawdę / chcesz, aby był skupiony). Jednak kluczem do zapamiętania w scenariuszach takich jak ten jest to, że Stackpanel jest elementem podrzędnym TreeViewItem, który w tym przypadku jest ItemContainer. Jak sugeruje Micah, poprawienie stylu pojemnika na przedmioty to dobre podejście.

Prawdopodobnie mógłbyś to zrobić za pomocą DataTemplates i rzeczy takich jak datatriggers, które używałyby rozszerzenia znaczników RelativeSouce do wyszukiwania elementu listviewitem

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.