ComboBox下拉列表中的项目和选定项目使用不同的模板

16

我有一个带有相当复杂模板的ComboBox,其中包括两个图像和几行文本:

enter image description here

然而,在ComboBox中选择的项本身显示不正确,因为垂直空间太小(我无法使其更高,因为它是ToolBar的一部分)。

如何使ComboBox在选择项时使用不同的模板来显示在ComboBox内?(默认的ToString表示法也可以)

谢谢!


使用DataTemplateSelector是否能为您解决问题,或者您正在寻找其他解决方案?也许我没有理解问题? - sll
@sll 我不认为DataTemplateSelector可以做到这一点(尽管我没有尝试过),如果我没记错的话,它只在加载项时被评估。无论如何,我更喜欢一些更简单的解决方案,不需要为我创建的每个ComboBox都涉及一个类(将有几个)。 - Matěj Zábský
你想要实现什么?根据某些条件,为项目提供不同的UI表示形式? - sll
@sll 从图片上可以看到,ComboBox 中当前选定的项目显示不正确。我只想修复它。 - Matěj Zábský
图片是如何绘制的?它们是从文件中加载还是使用WPF工具绘制的? - sll
@s11 图像是在运行时生成的,并转换为 BitmapSource,然后使用 WPF 的 Image 简单地显示。 - Matěj Zábský
2个回答

29

选择的项(在ComboBox本身中,而不是下拉列表中)未位于ComboBoxItem内,因此您可以像这样操作:

<ComboBox.ItemTemplate>
    <DataTemplate>
        <ContentControl Content="{Binding}">
            <ContentControl.Style>
                <Style TargetType="{x:Type ContentControl}">
                    <!-- Complex default template -->
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <Image Source="{Binding XPath=media:thumbnail/@url}" Width="100" Height="100" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                    <Style.Triggers>
                        <!-- Simple selection box template -->
                        <DataTrigger
                                Binding="{Binding RelativeSource={RelativeSource AncestorType=ComboBoxItem}}"
                                Value="{x:Null}">
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding XPath=title}" />
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </DataTemplate>
</ComboBox.ItemTemplate>

(编辑:请注意,选择框的绑定会引发错误,因为未找到RelativeSource。有多种解决这个问题的方法,其中之一是使用自定义值转换器,根据祖先是否存在返回truefalse(手动遍历树形结构)。

)

我在想WPF团队设计ComboBox时是怎么想的。克服这个问题的天真方法是什么(当数据模板在两个地方使用时:用于项目列表和在列表隐藏时选择的项目)?使用样式的ContentControl似乎是处理多个模板的常见hack,而不是使用DataTemplateSelector的糟糕想法,但我真的很讨厌检查祖先,这反过来也有问题,根据最新的编辑。 - Sinatr

11

我正在寻找这个问题的标准解决方案(不是hacky,也没有绑定错误),并在这里发现了它:使用DataTemplateSelector

这与@H.B.答案的想法相同:检查可视树中是否有ComboBoxItem作为父级。

public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate SelectedTemplate { get; set; }
    public DataTemplate DropDownTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        while (container != null)
        {
            container = VisualTreeHelper.GetParent(container);
            if (container is ComboBoxItem)
                return DropDownTemplate;
        }
        return SelectedTemplate;
    }
}

使用说明:

<ComboBox.ItemTemplateSelector>
    <local:ComboBoxItemTemplateSelector>
        <local:ComboBoxItemTemplateSelector.SelectedTemplate>
            <DataTemplate>
                ... simple template for selected item
            </DataTemplate>
        </local:ComboBoxItemTemplateSelector.SelectedTemplate>
        <local:ComboBoxItemTemplateSelector.DropDownTemplate>
            <DataTemplate>
                ... complex template used by dropdown items
            </DataTemplate>
        </local:ComboBoxItemTemplateSelector.DropDownTemplate>
    </local:ComboBoxItemTemplateSelector>
</ComboBox.ItemTemplateSelector>

1
选择后运行得很好。但在初始化时,SelectTemplate函数没有被调用。 - LWS
@LWS,什么是“on Init”?为什么你希望它在初始化时被调用而不是在组合框决定时被调用?这可能值得提出一个问题,如果没有[mcve],我怀疑能否提供帮助。 - Sinatr
当对话框加载时,我的组合框为空。第一次选择后一切正常工作。我只是使用您的代码而没有进行任何修改。 - LWS

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