自定义ComboBox模板忽略了DisplayMemberPath的使用

12

我在网站上看了好几个问题,但似乎找不到答案。

我有一个ComboBox。它之前一直工作得很好。后来我决定需要彻底改变它的外观,于是我创建了默认ComboBox模板的一个副本(这是一个完全的副本,没有修改):

<ControlTemplate x:Key="ComboBoxControlTemplate2" TargetType="{x:Type ComboBox}">
            <Grid x:Name="MainGrid" SnapsToDevicePixels="True">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
                </Grid.ColumnDefinitions>
                <Popup x:Name="PART_Popup" AllowsTransparency="True" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
                    <Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}">
                        <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                            <ScrollViewer x:Name="DropDownScrollViewer">
                                <Grid RenderOptions.ClearTypeHint="Enabled">
                                    <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                        <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
                                    </Canvas>
                                    <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </Grid>
                            </ScrollViewer>
                        </Border>
                    </Microsoft_Windows_Themes:SystemDropShadowChrome>
                </Popup>
                <ToggleButton BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
                    <ToggleButton.Style>
                        <Style TargetType="{x:Type ToggleButton}">
                            <Setter Property="OverridesDefaultStyle" Value="True"/>
                            <Setter Property="IsTabStop" Value="False"/>
                            <Setter Property="Focusable" Value="False"/>
                            <Setter Property="ClickMode" Value="Press"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                                        <Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" SnapsToDevicePixels="True">
                                            <Grid HorizontalAlignment="Right" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
                                                <Path x:Name="Arrow" Data="M0,0L3.5,4 7,0z" Fill="Black" HorizontalAlignment="Center" Margin="3,1,0,0" VerticalAlignment="Center"/>
                                            </Grid>
                                        </Microsoft_Windows_Themes:ButtonChrome>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsChecked" Value="True">
                                                <Setter Property="RenderPressed" TargetName="Chrome" Value="True"/>
                                            </Trigger>
                                            <Trigger Property="IsEnabled" Value="False">
                                                <Setter Property="Fill" TargetName="Arrow" Value="#FFAFAFAF"/>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </ToggleButton.Style>
                </ToggleButton>
                <ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="False" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="True">
                    <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
                    <Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
                </Trigger>
                <Trigger Property="HasItems" Value="False">
                    <Setter Property="Height" TargetName="DropDownBorder" Value="95"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                    <Setter Property="Background" Value="#FFF4F4F4"/>
                </Trigger>
                <Trigger Property="IsGrouping" Value="True">
                    <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
                </Trigger>
                <Trigger Property="CanContentScroll" SourceName="DropDownScrollViewer" Value="False">
                    <Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
                    <Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
现在,当我从我的列表(一个 POCO 集合)中选择一个项目时,它会显示命名空间和类名,而不是它应该显示的值。
我的研究和实验让我相信,问题在于我的新模板没有利用 DisplayMemberPath 属性。我尝试通过覆盖 OnDisplayMemberPathChanged 方法来设置 ItemTemplate,但这会在我从列表中选择一个项目时导致错误。
我还看到有人通过 XAML 设置 ItemTemplate,但我有数百个组合框,我不想这样做。
是否有一些方法可以在我的 ControlTemplate 中利用 DisplayMemberPath 属性,或者在派生控件中运行一些代码以实现我想要的结果?
5个回答

23

那不是一个完全的副本,这个元素缺少一个关键要素:

<ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
        Content="{TemplateBinding SelectionBoxItem}"
        ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
        IsHitTestVisible="False"
        Margin="{TemplateBinding Padding}"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />

也许你不小心把它删除了,即:

 ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
如果您没有设置这个,DisplayMemberPath 将无法工作,因为 ComboBox 使用模板选择器选择模板(就像您可以使用 ItemTemplateDisplayMemberPath 一样)。

哥们儿!那个完全起作用了。我从未见过那种解决方案,而且我百分之百确定它从未被生成过。我已经执行了这个过程3或4次,并检查了许多遇到同样问题的人。这肯定是微软方面的一个漏洞...谢谢! - Camron B
现在兴奋的情绪已经平息了,我必须问一下你是如何想到这个的。你使用什么来生成模板副本? - Camron B
3
@CamronBute:我总是有在MSDN上获取的默认模板(“Default WPF Themes”链接),所以我查看了一下,看看事情是如何绑定的。很容易看出,这个 ContentPresenter 是选定项目的关键组件,因此如果有错误应该在那里,然后我只需要将其与你的进行比较,就可以发现这个属性缺失了。我还测试了删除该属性的效果,并如预期地只显示了类型名称。 - H.B.
我以前从未见过那个链接。我一直使用Blend。谢谢你的帮助和链接! - Camron B
3
我可以确认,在VS2012中,“复制模板”的混合功能确实存在这种怪异行为。 - dansan
显示剩余2条评论

3
请确保您没有遗漏ContentTemplateSelector元素。
对于组合框模板本身应该是:
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"

对于ItemsContainerStyle,它应该是这样的:
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"        

1

我曾经有同样的问题, 结果发现设置DisplayMemberPath只是一种快捷方式,可以将ItemTemplate设置为包含该值的TextBlock

因此,当您设置了ItemTemplateDisplayMemberPath就变得无用了,因为您已经覆盖了默认的包含值的TextBlock。


请检查上面的答案。我在一个相关的问题中看到了你的问题,但是上面的解决方案对我有效!无论如何还是谢谢你 :) - Camron B
我使用了H.B.的解决方案,但在此答案之后,尝试ContentTemplate="{TemplateBinding ItemTemplate}"也起作用了 - 尽管我没有费心留下足够的时间来测试任何副作用。 - OhBeWise

0

我知道这是一个老问题,但最近我也遇到了同样的问题,并通过添加

ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"

而不是

ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"

来解决它。


0

我遇到了同样的问题,这应该是一个bug,我会尝试报告它。

当你从Visual Studio中“编辑控件模板”的副本时,它会缺少以下行:

ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"

这导致“DisplayMemberPath”无法正常工作。


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