XAML 为什么这个ComboBox将SelectedItem直接呈现为内容,而不是使用ItemTemplate?

mlnl4t2r  于 2023-09-28  发布在  其他
关注(0)|答案(2)|浏览(138)

我有一个ComboBox显示项目列表。我创建了一个转换器,将此对象类型Map到string值。

<ComboBox ItemsSource="{Binding SelectedDrawing.Icons}" Name="TrackElementSelector" SelectedItem="{Binding SelectedElement}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=., Converter={StaticResource LayoutElementToStringConverter}}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

问题是,当我选择/单击ComboBox中的一个值时,ComboBox直接呈现SelectedItem(它是在我的项目中其他地方定义的UserControl)。例如,想象一下在ComboBox上单击字符串元素“Button”后看到文字Button。我不想要这种行为。
为什么这里没有使用ItemTemplateItemTemplate似乎只在选择某个值之前使用。
我发现了另一个意想不到的结果。当我下拉ComboBox进行选择时,控件出现在应用程序其他位置的示例就会消失。我想知道是否由于我的代码库中的东西而发生了一些更复杂的事情,或者这是试图在ComboBox中显示UserControl对象的所有后果。

8cdiaqws

8cdiaqws1#

您发现了ComboBox控件的一个有趣行为。绑定项时,项的呈现方式有时取决于其类型。例如,如果你绑定的项目已经是ComboBoxItem类型,它将不会创建额外的容器,因为这将是多余的。这也是其他ItemsControl的常见设计决策。

为什么忽略项目模板

但是,您的问题源于ComboBox中所选项目的不同特殊处理(关于ContentControl s)。如果所选项目是这种类型或其任何直接或间接衍生物(如UserControl),则ComboBox将使用其ContentContentTemplateContentStringFormat,而不是为ComboBox本身上的项目定义的ItemTemplateItemStringFormat,从而忽略ItemTemplate

实现详情

您可以在UpdateSelectionBoxItem方法中的ComboBox实现中看到这一点:

// if Items contains an explicit ContentControl, use its content instead
// (this handles the case of ComboBoxItem)
ContentControl contentControl = item as ContentControl;

if (contentControl != null)
{
    item = contentControl.Content;
    itemTemplate = contentControl.ContentTemplate;
    stringFormat = contentControl.ContentStringFormat;
}

我不知道为什么要这样实现它,但最初的评论表明,这可能是为了处理绑定项已经是ComboBoxItem的情况。也许这种行为不是故意的,而是偶然的。

解决方案

如果你想改变这种行为,你有这些选项:
1.创建您自己的ComboBox自定义实现。
1.将项目模板也添加到UserControl作为内容模板。
虽然它与给定的实现一起工作,但它是一种黑客。
1.创建忽略特殊处理的自定义控件模板。
您将创建默认样式和控件模板的副本,并调整其ContentPresenter。默认情况下,它绑定到特殊的SelectionBoxItemTemplate属性。您可以通过绑定到ItemTemplate属性来忽略它。

<ContentPresenter x:Name="contentPresenter"
                  Content="{TemplateBinding SelectedItem}"
                  ContentTemplate="{TemplateBinding ItemTemplate}"
                  ...>

1.不要对项使用ContentControl或派生类型,而是使用带有 data 项的数据模板作为源,这是预期的,请参见Data Templating Overview
您不应该将UI元素绑定为ComboBox的项目源。相反,绑定封装在模型中的数据,并提供合适的数据模板来显示它。数据可以是一个带有Path属性的Icon类,甚至可以是一个表示路径的string
数据模板可以重用您的UserControl类型来显示它(如果它被正确实现为使用其数据上下文绑定数据)。这将导致更好的设计,并且很可能也消除对值转换器的需要。此外,它将自动解决您的第二个问题,消失的控件。

我强烈建议您使用此选项,因为它不需要对框架元素进行任何自定义,并且更容易执行和维护。

控件为何消失

在WPF中,UI控件被附加到所谓的visual tree,这是用户界面的层次对象图表示。在此图中,每个UI元素示例只能有单个父级。因此,如果你在不同的地方“使用”一个现有的控件,它会被有效地重新分配给相应的父控件,无论它是在同一个还是不同的可视树中(例如,另一个窗口)和 “消失” 从原来的地方。

plicqrtu

plicqrtu2#

源集合(SelectedDrawing.Icons)不应该包含UIElements。则不应用ItemTemplate
UserControlButton应该在ItemTemplate中定义:

<ComboBox ItemsSource="{Binding SelectedDrawing.Icons}" Name="TrackElementSelector" 
          SelectedItem="{Binding SelectedElement}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <local:UserControl1 />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

源集合应该包含普通的旧CLR对象(POCO),例如:

public class Icon
{
    public string Name { get; set; }
}

另请注意,控件的示例在可视树中只能出现 * 一次 *。

相关问题