Ubah WPF DataTemplate untuk item ListBox jika dipilih

91

Saya perlu mengubah DataTemplate untuk item dalam ListBox tergantung pada apakah item tersebut dipilih atau tidak (menampilkan informasi yang berbeda / lebih banyak saat dipilih).

Saya tidak mendapatkan acara GotFocus / LostFocus di elemen paling atas di DataTemplate (sebuah StackPanel) saat mengklik item ListBox yang dimaksud (hanya melalui tab), dan saya kehabisan ide.

Daniel Beck
sumber

Jawaban:

184

Cara termudah untuk melakukannya adalah dengan menyediakan template untuk "ItemContainerStyle" dan BUKAN properti "ItemTemplate". Dalam kode di bawah ini saya membuat 2 template data: satu untuk "tidak dipilih" dan satu untuk negara "dipilih". Saya kemudian membuat template untuk "ItemContainerStyle" yang merupakan "ListBoxItem" sebenarnya yang berisi item tersebut. Saya menyetel default "ContentTemplate" ke status "Tidak dipilih", dan kemudian memberikan pemicu yang menukar template saat properti "IsSelected" benar. (Catatan: Saya menyetel properti "SumberSumber" di kode di belakang ke daftar string untuk kesederhanaan)

<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}" />
Mikha
sumber
1
Terima kasih, harap sertakan <ListBox ItemContainerStyle = ”{StaticResource ContainerStyle}” ItemsSource = ”{Binding MyData}” /> di kiriman Anda, sehingga orang tidak perlu mencari di blog Anda.
Shimmy Weitzhandler
2
Satu masalah yang saya temui dengan pengaturan ContainerStyle ListBox adalah hal itu menyebabkan ketidakcocokan dengan tema. Saya menggunakan pendekatan Anda, tetapi ketika saya menerapkan tema dari WPF Futures mengatur ListBoxItems memiliki gaya default alih-alih gaya tema. Dalam kasus saya, teks hitam dengan latar belakang hitam dan keburukan umum. Saya masih mencari pendekatan lain, mungkin menggunakan pemicu DataTemplate.
Benny Jobigan
1
Selain itu, jika Anda ingin ItemContainerStyle baru Anda kompatibel dengan tema, Anda harus mendasarkannya pada salah satu tema. Untuk melakukan ini, gunakan BasedOn="{StaticResource {x:Type ListBoxItem}}"dengan ListBox. Ini juga berlaku untuk kontrol lain seperti TreeView.
Benny Jobigan
5
Dalam menggunakan ini saya menemukan saya harus mendeklarasikan DataTemplates di atas Style di bagian Resources agar tidak mendapatkan kesalahan XAML misterius. Hanya informasi sebelumnya tentang itu.
Rob Perkins
8

Untuk menyetel gaya saat item dipilih atau tidak, yang perlu Anda lakukan adalah mengambil ListBoxIteminduk di Anda <DataTemplate>dan gaya pemicu berubah saat IsSelectedberubah. Misalnya kode di bawah ini akan membuat TextBlockdengan Foregroundwarna default hijau . Sekarang jika item dipilih, font akan berubah menjadi merah dan ketika mouse di atas item akan menjadi kuning . Dengan begitu, Anda tidak perlu menentukan templat data terpisah seperti yang disarankan dalam jawaban lain untuk setiap status yang ingin Anda ubah sedikit.

<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
sumber
1
Seperti yang saya tulis di pertanyaan, saya sebenarnya menampilkan lebih banyak informasi jika dipilih (" menampilkan informasi berbeda / lebih banyak saat dipilih "). Namun, jika ini dapat dibuat untuk bekerja dengan mengubah visibilitas beberapa elemen (termasuk apakah ukurannya lebih besar), ini akan menjadi solusi yang layak. Belum bekerja dengan WPF dalam beberapa saat.
Daniel Beck
6

Perlu juga dicatat, bahwa stackpanel tidak dapat difokuskan, sehingga tidak akan pernah mendapatkan fokus (setel Focusable = True jika Anda / benar-benar / ingin fokus). Namun, kunci untuk mengingat dalam skenario seperti ini adalah bahwa stackpanel adalah anak dari TreeViewItem, yang merupakan ItemContainer dalam kasus ini. Seperti yang dikatakan Mikha, mengutak-atik itemcontainerstyle adalah pendekatan yang baik.

Anda mungkin bisa melakukannya menggunakan DataTemplates, dan hal-hal seperti datatriggers yang akan menggunakan ekstensi markup RelativeSouce untuk mencari listviewitem

Dominic Hopton
sumber