WPF ComboBox - 选择某个值时显示不同的内容

8
我需要实现一个下拉框,用于显示人员信息。展开下拉框后,应该显示名字和姓氏,但是当你选择一个人时,下拉框中显示的值应该只有这个人的名字。
我已经准备好了以下的ItemTemplate:
<ComboBox.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding FirstName}" />
            <TextBlock Text=" " />
            <TextBlock Text="{Binding LastName}" />
        </StackPanel>
    </DataTemplate>
</ComboBox.ItemTemplate>

当仅选择一个项目时,我应该做什么才能仅显示名字?

谢谢!

编辑

稍微修改了问题:如果我有这个人的照片,而不是在选择一个人时仅显示名字,我想仅显示照片。换句话说,如何拥有两个单独的模板 - 一个用于下拉菜单,一个用于所选项目?


有点晚了,但我认为这与此相关。在此处描述了一种完全不同的解决方案:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0467c9ca-efb2-4506-96e7-08ce3356860a/。没有自定义代码,也可以很好地工作。 - Herman Cordes
5个回答

19

这是解决方案:

    <ComboBox>
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <ContentControl x:Name="content" Content="{Binding}" ContentTemplate="{StaticResource ComplexTemplate}"/>
                </StackPanel>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBoxItem}}" Value="{x:Null}">
                        <Setter TargetName="content" Property="ContentTemplate" Value="{StaticResource SimpleTemplate}"/>
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

基本上,这里创建了一个数据模板的另一层。ComboBox的ItemTemplate始终保持不变。但该模板中的内容会根据您所关心的条件进行调整。

区分下拉combobox项和选定区域combobox项的技巧是,选定区域实际上并没有包含在ComboBoxItem对象中,它是ComboBox控件本身的一部分。因此,在上面的触发器中,FindAncestor用于ComboBoxItem返回null。


1
唯一的问题是它会导致 WPF 触发绑定错误,因为找不到祖先。 - scobi

6

我明白了。我只需要在我的ComboBox中添加以下内容:

IsEditable="True" IsReadOnly="True" TextSearch.TextPath="FirstName"

这解决了我的最初问题,但我稍微改了一下。当选择一个项目时,如何只显示图片而不是名字? - Gus Cavalcanti
谢谢,需要一个简单的解决方案。 - TheZenker
1
太棒了,正是我想要的! - Michal Ciechan

5

在DataTemplate上放置一个触发器。该触发器应检查IsSelected属性(为使其起作用,DataTemplate需要设置TargetType)。如果选中了该属性,则可以将TextBlocks的可见性设置为Collapsed,并将Image的可见性设置为Visible。对于未选中的情况,请执行相反的操作。


谢谢。这很有道理,因为在ComboBox中只能选择一个项目。好主意。 - Gus Cavalcanti

2
另一个选项是使用ItemTemplateSelector代替ItemTemplate。我一直是这样使用的。 ComboBoxItemTemplateSelector派生自DataTemplateSelector,具有两个附加属性:SelectedTemplateDropDownTemplate。然后我们可以像这样从Xaml设置DataTemplates:
<ComboBox ItemsSource="{Binding Persons}"
          ItemTemplateSelector="{StaticResource ComboBoxItemTemplateSelector}">
    <ts:ComboBoxItemTemplateSelector.SelectedTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding FirstName}" />
        </DataTemplate>
    </ts:ComboBoxItemTemplateSelector.SelectedTemplate>
    <ts:ComboBoxItemTemplateSelector.DropDownTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding FirstName}" />
                <TextBlock Text=" " />
                <TextBlock Text="{Binding LastName}" />
            </StackPanel>
        </DataTemplate>
    </ts:ComboBoxItemTemplateSelector.DropDownTemplate>
</ComboBox>

在SelectTemplate中,我们检查当前容器是否包含在ComboBoxItem中,如果是,则返回DropDownTemplate。否则,返回SelectedTemplate
public class ComboBoxItemTemplateChooser : DataTemplateSelector
{
    #region SelectedTemplate..
    #region DropDownTemplate..

    public override DataTemplate SelectTemplate(object item,
                                                DependencyObject container)
    {
        ComboBox parentComboBox = null;
        ComboBoxItem comboBoxItem = container.GetVisualParent<ComboBoxItem>();
        if (comboBoxItem == null)
        {
            parentComboBox = container.GetVisualParent<ComboBox>();
            return ComboBoxItemTemplateChooser.GetSelectedTemplate(parentComboBox);
        }
        parentComboBox = ComboBox.ItemsControlFromItemContainer(comboBoxItem) as ComboBox;
        return ComboBoxItemTemplateChooser.GetDropDownTemplate(parentComboBox);
    }
}

这里可以下载一个使用它的小型演示项目:ComboBoxItemTemplateDemo.zip。我在这里也写了一篇简短的博客文章:不同的下拉框 ComboBox ItemTemplate。它还展示了另一种明显的方法,即在ComboBoxItemTemplateSelector中使用属性而不是附加属性。噢,还有 GetVisualParent。似乎每个人都有自己的实现方式,但无论如何,这是我正在使用的一个实现。
public static class DependencyObjectExtensions
{
    public static T GetVisualParent<T>(this DependencyObject child) where T : Visual
    {
        while ((child != null) && !(child is T))
        {
            child = VisualTreeHelper.GetParent(child);
        }
        return child as T;
    }
}

0

我使用了下一个方法

 <UserControl.Resources>
    <DataTemplate x:Key="SelectedItemTemplate" DataType="{x:Type statusBar:OffsetItem}">
        <TextBlock Text="{Binding Path=ShortName}" />
    </DataTemplate>
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
    <ComboBox DisplayMemberPath="FullName"
              ItemsSource="{Binding Path=Offsets}"
              behaviors:SelectedItemTemplateBehavior.SelectedItemDataTemplate="{StaticResource SelectedItemTemplate}"
              SelectedItem="{Binding Path=Selected}" />
    <TextBlock Text="User Time" />
    <TextBlock Text="" />
</StackPanel>

并且这种行为

public static class SelectedItemTemplateBehavior
{
    public static readonly DependencyProperty SelectedItemDataTemplateProperty =
        DependencyProperty.RegisterAttached("SelectedItemDataTemplate", typeof(DataTemplate), typeof(SelectedItemTemplateBehavior), new PropertyMetadata(default(DataTemplate), PropertyChangedCallback));

    public static void SetSelectedItemDataTemplate(this UIElement element, DataTemplate value)
    {
        element.SetValue(SelectedItemDataTemplateProperty, value);
    }

    public static DataTemplate GetSelectedItemDataTemplate(this ComboBox element)
    {
        return (DataTemplate)element.GetValue(SelectedItemDataTemplateProperty);
    }

    private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var uiElement = d as ComboBox;
        if (e.Property == SelectedItemDataTemplateProperty && uiElement != null)
        {
            uiElement.Loaded -= UiElementLoaded;
            UpdateSelectionTemplate(uiElement);
            uiElement.Loaded += UiElementLoaded;

        }
    }

    static void UiElementLoaded(object sender, RoutedEventArgs e)
    {
        UpdateSelectionTemplate((ComboBox)sender);
    }

    private static void UpdateSelectionTemplate(ComboBox uiElement)
    {
        var contentPresenter = GetChildOfType<ContentPresenter>(uiElement);
        if (contentPresenter == null)
            return;
        var template = uiElement.GetSelectedItemDataTemplate();
        contentPresenter.ContentTemplate = template;
    }


    public static T GetChildOfType<T>(DependencyObject depObj)
        where T : DependencyObject
    {
        if (depObj == null) return null;

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);

            var result = (child as T) ?? GetChildOfType<T>(child);
            if (result != null) return result;
        }
        return null;
    }
}

非常好用。虽然这里加载事件不太理想,但如果您愿意,可以修复它。


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