如何在保留 DisplayMemberPath 的情况下重写 ListBox 的 ItemTemplate?

11

我有一个通用的样式,适用于 ListBox,它覆盖了 ItemTemplate 并使用 RadioButtons。 它非常好用,但是当我设置了 DisplayMemberPath 后,就只能够获取到 ListBox 中每个项的 .ToString()

我感觉我在这里错过了一些简单的东西......有人可以帮我找出来吗?

<Style x:Key="RadioButtonListBoxStyle" TargetType="{x:Type ListBox}">
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Cycle" />
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="{x:Type ListBoxItem}" >
                <Setter Property="Margin" Value="2, 2, 2, 0" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Border Background="Transparent">
                                <RadioButton
                                    Content="{TemplateBinding ContentPresenter.Content}" VerticalAlignment="Center"
                                    IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"/>

                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

我的ListBox被绑定到一个List<T>KeyValuePairs。如果我移除样式,DisplayMemberPath就能正确显示,所以问题肯定出在样式上。

<ListBox Style="{StaticResource RadioButtonListBoxStyle}"
         ItemsSource="{Binding MyCollection}"
         DisplayMemberPath="Value" SelectedValuePath="Key" />

有点晚了,但你试过使用<RadioButton><RadioButton.Content><ContentPresenter /></RadioButton.Content></RadioButton>吗?不将Content属性定义为属性,而是作为XElement允许您使用Contentpresenter对象,在我的情况下(在listbox上)返回了DisplayMemberpath属性中定义的属性值。没有测试,只是一个想法。 - dba
4个回答

11
为什么你想要保留DisplayMemberPath? 它只是一个ItemTemplate的快捷方式,其中包含一个TextBlock以显示DisplayMemberPath中的值。 使用自己的ItemTemplate可以更灵活地决定如何以及以何种方式进行显示。
只需在ItemTemplate中添加一个TextBlock,并设置 Text="{Binding Value}",您就可以得到所需的结果。
正如这里所述:

此属性是定义默认模板的简单方法,该模板描述了如何显示数据对象。

DisplayMemberPath提供了一种简单的方法来设置模板,但是您需要不同的模板。 您不能也不需要同时使用两种。

3
因为这个样式在大约5个不同的地方使用,所以我想要一个通用的ItemTemplate样式。 - Rachel
首先,我不会覆盖那个样式,我会为您的不同数据类型提供一个DataTemplate。这样,您就不需要因为数据而更改样式。ItemContainerStyle应该只包含外观,DataTemplate应该包含数据的显示方式。 - dowhilefor
我正在覆盖样式,因为有多个不同级别的属性我想要设置,而且我不想写三个不同的绑定来使ListBox看起来像一个ComboBox。但是你的答案是无法同时设置DisplayMemberPathItemTemplate吗? - Rachel
我重新阅读了你编辑后的内容,现在更有意义了。 - Rachel
2
是的,至少这是有意义的。就像我说的那样,DisplayMemberPath 只是一种语法糖,为您的项提供 DataTemplate,添加 TextBlock 并将 Text 绑定设置为 DisplayMemberPath 绑定。如果您提供自己的模板,这将不再起作用。 - dowhilefor

5

我仍然不知道如何使用DisplayMemberPath进行绘制,但是我找到了缺失的部分,可以使用ItemTemplate进行绘制 - 我需要ContentTemplate绑定。

<RadioButton 
    Content="{TemplateBinding ContentPresenter.Content}" 
    ContentTemplate="{TemplateBinding ContentPresenter.ContentTemplate}"
    IsChecked="{Binding Path=IsSelected,RelativeSource={
                        RelativeSource TemplatedParent},Mode=TwoWay}" />

那么在我的XAML中,我可以这样写:

<ListBox Style="{StaticResource RadioButtonListBoxStyle}"
         ItemsSource="{Binding MyCollection}"
         SelectedValuePath="Key">

    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Value}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

感谢 dowhilefor 指出,DisplayMemberPath 只是写 ItemTemplate 的一种快捷方式,两者不能同时设置。

只是顺便提一下,您还应该设置ContentTemplateSelector,就像设置Content和ContentTemplate一样。在您的情况下可能不需要它,但值得指出的是,这是提供项目模板的另一种方式,我记得我们曾经忘记了一个错误 ;) - dowhilefor

3

早上开始我一直在研究这个问题,这个帖子对我帮助很大。我调查了解决方案,发现即使在定义自定义ListBoxItem控件模板的样式中,仍然有一种方式支持DisplayMemberPath。关键是添加

    ContentTemplateSelector="{TemplateBinding ContentControl.ContentTemplateSelector}"

针对本线程中的RadioButton,请将其发送给ContentPresenter。

这种方法有效的原因是,内部的displaymemberpath属性会导致Listbox将ContentTemplateSelector指定为“ DisplayMemberTemplateSelector”模板选择器。如果没有此TemplateBinding,则该选择器不起作用。

祝好!


有道理,我稍后会试一下。谢谢! - Rachel

0

简短版

设置一个带有ContentPresenter的ItemContainerStyle,并确保不覆盖ItemTemplate。

需求

您想定义一个通用样式以供重复使用。

您想将其与不同的数据类型一起重复使用,因此您希望能够在重复使用时使用DisplayMemberPath或ItemTemplate - 而无需重新定义整个样式。

问题

您没有在项目的容器上使用ContentPresenter。
项目本身将无处可绘制。

解决方案

针对您的具体情况

在RadioButton中放置ContentPresenter;那应该可以解决问题:

<RadioButton IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
             VerticalAlignment="Center">
    <ContentPresenter />
</RadioButton>

一般来说

定义ItemContainerStyle。这样可以控制每个项的包装方式。

该样式针对ListBoxItem。

定义该样式的模板,确保包括ContentPresenter(其中将显示项目本身的内容)。

一个示例

定义样式
<Style TargetType="{x:Type ListBoxItem}" x:Key="BigListBoxItemStyle" BasedOn="{StaticResource DefaultListBoxItemStyle}">
    <Setter Property="Foreground" Value="DeepPink" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="Height" Value="71" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="BorderThickness" Value="1" />
    <Setter Property="BorderBrush" Value="Transparent" />
    <Setter Property="Padding" Value="10 5 10 5" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <Border Background="{TemplateBinding Background}"
                        Margin="{TemplateBinding Margin}"
                        BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*" />
                            <RowDefinition Height="1" />
                        </Grid.RowDefinitions>
                        <ContentPresenter Grid.Row="0" Grid.Column="0"
                                          Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="Center" />
                        <Rectangle x:Name="GraySeparator"
                                   Grid.Row="1"
                                   Height="1" Stroke="Gray" Opacity="0.2"
                                   HorizontalAlignment="Stretch" VerticalAlignment="Bottom" />
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected" Value="True" >
                        <Setter Property="Background" Value="Yellow" />
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="True" >
                        <Setter Property="BorderBrush" Value="DarkGreen" />
                        <Setter Property="Visibility" Value="Hidden" TargetName="GraySeparator" />
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Opacity" Value=".5" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="BigListBoxStyle" TargetType="{x:Type ListBox}">
    <Setter Property="ItemContainerStyle" Value="{StaticResource BigListBoxItemStyle}" />
</Style>

使用它
<ListBox Style="{StaticResource BigListBoxStyle}"
         ItemsSource="{Binding MyTuples}"
         DisplayMemberPath="Item2"
         SelectedItem="{Binding SelectedTuple}">

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