如果选中,更改ListBox项目的WPF DataTemplate

93

我需要根据所选项目的状态(选中或未选中)更改ListBox中项的DataTemplate (在选中时显示不同/更多信息)。

当单击相关的ListBox项(仅通过制表符),我无法在DataTemplate中的最上层元素(StackPanel)上获得GotFocus / LostFocus事件,我已经没有其他想法。

3个回答

187

最简单的方法是提供“ItemContainerStyle”而不是“ItemTemplate”属性的模板。在下面的代码中,我创建了两个数据模板:一个用于“未选择”状态,另一个用于“已选择”状态。然后我创建了一个“ItemContainerStyle”的模板,它是包含项的实际“ListBoxItem”。我将默认的“ContentTemplate”设置为“Unselected”状态,然后提供一个触发器,在“IsSelected”属性为true时切换模板。(注意:出于简单起见,我在代码后台将“ItemsSource”属性设置为字符串列表)

<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
谢谢,请在您的帖子中包含 <ListBox ItemContainerStyle=”{StaticResource ContainerStyle}” ItemsSource=”{Binding MyData}” />,这样人们就不必在您的博客中搜索了。 - Shimmy Weitzhandler
2
我在设置ListBox的ContainerStyle时遇到的一个问题是它会导致与主题不兼容。我使用了你的方法,但当我应用WPF Futures集合中的主题时,ListBoxItems具有默认样式而不是主题样式。在我的情况下,黑色文本和黑色背景以及一般的丑陋。我仍在寻找另一种方法,也许可以使用DataTemplate触发器。 - Benny Jobigan
1
此外,如果您希望新的ItemContainerStyle与主题兼容,您必须以主题中的样式为基础。为此,请在ListBox中使用BasedOn = "{StaticResource {x:Type ListBoxItem}}"。这也适用于其他控件,如TreeView。 - Benny Jobigan
5
使用这个时,我发现必须在资源部分中先声明DataTemplates,再声明Style,否则会出现晦涩难懂的XAML错误。只是提醒一下。 - Rob Perkins

15

要在选定或未选定项目时设置样式,您只需要在<DataTemplate>中检索ListBoxItem父级,并在其IsSelected更改时触发样式更改。例如,下面的代码将创建一个带有默认Foreground颜色为绿色TextBlock。现在,如果选择该项,则字体将变为红色,当鼠标悬停在该项上时,该项将变为黄色。这样,您无需为每种状态指定单独的数据模板(如其他答案所建议)就可以轻微更改。

<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
正如我在问题中所写的,如果被选中,我实际上会显示更多信息。不过,如果这可以通过切换某些元素的可见性(包括它们占用空间的大小)来实现,那么这将是一个可行的解决方案。虽然我有一段时间没有使用 WPF 了。 - Daniel Beck

6
需要翻译的内容如下:

需要注意的是,stackpanel无法获取焦点,因此它永远不会获得焦点(如果您真的想要它获得焦点,请设置Focusable=True)。但是,在这种情况下,需要记住的关键是Stackpanel是TreeViewItem的子项,而TreeViewItem是ItemContainer。正如Micah建议的那样,调整itemcontainerstyle是一个很好的方法。

您可以使用DataTemplates和datatriggers等内容来完成此操作,这些内容将使用RelativeSouce标记扩展来查找listviewitem。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接