WPF ComboBox - Показывает что-то другое при выборе значения - PullRequest
7 голосов
/ 17 июля 2009

Что мне нужно сделать, так это ComboBox, который показывает людей. При раскрытии раскрывающегося списка отображаются FirstName и LastName, но при выборе человека значение, отображаемое в поле со списком, должно быть просто именем человека.

У меня есть следующий шаблон элемента:

<ComboBox.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding FirstName}" />
            <TextBlock Text=" " />
            <TextBlock Text="{Binding LastName}" />
        </StackPanel>
    </DataTemplate>
</ComboBox.ItemTemplate>

Что еще нужно сделать, чтобы отображалось только имя при выборе одного элемента?

Спасибо!

EDIT

Немного изменил вопрос: что, если у меня есть изображение человека, и вместо того, чтобы показывать только имя, когда человек выбран, я хочу показать только изображение. Другими словами, как я могу иметь два отдельных шаблона - один для раскрывающегося списка и один для выбранного элемента?

Ответы [ 5 ]

18 голосов
/ 20 июля 2009

Вот решение:

    <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>

По сути, вы создаете еще один слой DataTemplate здесь. ItemTemplate ComboBox'а всегда остается неизменным. Но содержимое этого шаблона подстраивается под интересующее вас условие.

Хитрость в различении выпадающих элементов комбинированного списка и элемента комбинированного списка выбранной области заключается в том, что выбранная область на самом деле не заключена в объект ComboBoxItem, а является частью самого элемента управления ComboBox. Поэтому FindAncestor для ComboBoxItem возвращает значение null, которое мы используем в триггере выше.

6 голосов
/ 17 июля 2009

Я понял. Мне просто нужно добавить следующее в мой ComboBox:

IsEditable="True" IsReadOnly="True" TextSearch.TextPath="FirstName"
5 голосов
/ 17 июля 2009

Поместите триггер на шаблон данных. Триггер должен проверить свойство IsSelected (для DataTemplate потребуется набор TargetType, чтобы это работало). Если он выбран, вы можете установить Видимость ваших TextBlocks на Свернутый, и установить Видимость изображения на Видимый. Затем сделайте обратное для случая, когда он не выбран.

1 голос
/ 14 мая 2012

Другой вариант - использовать ItemTemplateSelector вместо ItemTemplate. Я использовал это следующим образом.

ComboBoxItemTemplateSelector происходит от DataTemplateSelector и имеет два прикрепленных свойства, SelectedTemplate и DropDownTemplate. Затем мы устанавливаем DataTemplates из Xaml следующим образом

<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 для выпадающего списка . Также показан другой очевидный способ сделать то же самое, но со свойствами вместо прикрепленных свойств в 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 голосов
/ 02 мая 2013

Я использовал следующий подход

 <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;
    }
}

работал как шарм. Не очень понравилось событие Loaded, но вы можете это исправить, если хотите

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...