Как отобразить другое значение для выпадающего списка значений / выбранного элемента в ComboBox WPF? - PullRequest
12 голосов
/ 22 октября 2010

У меня есть комбинированный список WPF, связанный со списком элементов с длинными описаниями.

Тип, связанный с ComboBox, имеет как краткое, так и длинное описание в качестве свойств.В настоящее время я привязываюсь к полному описанию.

comboBox.DisplayMemberPath = "FullDescription";

Как обеспечить, чтобы, когда элемент был выбран и отображался как отдельный элемент в выпадающем списке, он отображался как значение ShortDescriptionв то время как раскрывающийся список будет отображать FullDescription?

Ответы [ 5 ]

17 голосов
/ 22 октября 2010

Обновление 2011-11-14

Недавно я снова столкнулся с тем же требованием, и я не был очень доволен решением, которое я разместил ниже. Вот более хороший способ получить то же поведение без повторной настройки ComboBoxItem. Он использует DataTemplateSelector

Сначала укажите обычный DataTemplate, раскрывающийся список DataTemplate и ComboBoxItemTemplateSelector в ресурсах для ComboBox. Затем укажите ComboBoxItemTemplateSelector как DynamicResource для ItemTemplateSelector

<ComboBox ...
          ItemTemplateSelector="{DynamicResource itemTemplateSelector}">
    <ComboBox.Resources>
        <DataTemplate x:Key="selectedTemplate">
            <TextBlock Text="{Binding Path=ShortDescription}"/>
        </DataTemplate>
        <DataTemplate x:Key="dropDownTemplate">
            <TextBlock Text="{Binding Path=FullDescription}"/>
        </DataTemplate>
        <local:ComboBoxItemTemplateSelector
            x:Key="itemTemplateSelector"
            SelectedTemplate="{StaticResource selectedTemplate}"
            DropDownTemplate="{StaticResource dropDownTemplate}"/>
    </ComboBox.Resources>
</ComboBox>

ComboBoxItemTemplateSelector проверяет, является ли контейнер дочерним для ComboBoxItem, если это так, то мы имеем дело с выпадающим элементом, в противном случае это элемент в ComboBox.

public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate DropDownTemplate
    {
        get;
        set;
    }
    public DataTemplate SelectedTemplate
    {
        get;
        set;
    }
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        ComboBoxItem comboBoxItem = VisualTreeHelpers.GetVisualParent<ComboBoxItem>(container);
        if (comboBoxItem != null)
        {
            return DropDownTemplate;
        }
        return SelectedTemplate;
    }
}

GetVisualParent

public static T GetVisualParent<T>(object childObject) where T : Visual
{
    DependencyObject child = childObject as DependencyObject;
    while ((child != null) && !(child is T))
    {
        child = VisualTreeHelper.GetParent(child);
    }
    return child as T;
}

Старое решение, требует повторного шаблонирования ComboBoxItem

<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

<ControlTemplate x:Key="FullDescriptionTemplate" TargetType="ComboBoxItem">
    <Border Name="Border" Padding="2" SnapsToDevicePixels="true">
        <StackPanel>
            <TextBlock Text="{Binding Path=FullDescription}"/>
        </StackPanel>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsHighlighted" Value="true">
            <Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="false">
            <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<ComboBox Name="c_comboBox" ItemsSource="{Binding}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=ShortDescription}"/>
        </DataTemplate>
    </ComboBox.ItemTemplate>
    <ComboBox.ItemContainerStyle>
        <Style TargetType="{x:Type ComboBoxItem}">
            <Setter Property="Template" Value="{StaticResource FullDescriptionTemplate}" />
        </Style>
    </ComboBox.ItemContainerStyle>
</ComboBox>

Это приводит к следующему поведению

alt text

2 голосов
/ 28 апреля 2015

Кажется, он сейчас не работает, но вот этот:

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

  public override DataTemplate SelectTemplate(object item, DependencyObject container) {
    var presenter = (ContentPresenter)container;
    return (presenter.TemplatedParent is ComboBox) ? SelectedTemplate : DropDownTemplate;
  }
}
1 голос
/ 23 июля 2018

Я изменил этот пользовательский скругленный комбинированный список WPF, чтобы отобразить другое значение из выбранного элемента, а также изменить цвет для каждого элемента.

Пользовательский комбинированный список

Первыйвам нужно создать структуру:

//Structure
public class COMBOITEM
{
    string _ITEM_NAME;
    string _ITEM_SHORT_NAME;
    Brush _ITEM_COLOR;

    public string ITEM_NAME
    {
        get { return _ITEM_NAME; }
        set { _ITEM_NAME = value; }
    }

    public string ITEM_SHORT_NAME
    {
        get { return _ITEM_SHORT_NAME; }
        set { _ITEM_SHORT_NAME = value; }
    }

    public Brush ITEM_COLOR
    {
        get { return _ITEM_COLOR; }
        set { _ITEM_COLOR = value; }
    }
}

Инициализировать структуру, заполнить ее данными и привязать к ComboBox:

private void Load_Data()
{
    Brush Normal_Blue = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF1F4E79"));
    //Load first entry
    ObservableCollection<COMBOITEM> _Line_Data = new ObservableCollection<COMBOITEM>();
    _Line_Data.Add(new COMBOITEM() { ITEM_NAME = "Line Number 1", ITEM_SHORT_NAME = "LN 1", ITEM_COLOR = Normal_Blue });

    //Load Test Data
    for (int i = 2; i < 10; i++)
    {
        _Line_Data.Add(new COMBOITEM()
        {
            ITEM_NAME = "Line Number " + i.ToString(),
            ITEM_SHORT_NAME = "LN " + i.ToString(),
            ITEM_COLOR = (i % 2 == 0) ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red) //This just changes color
        });
    }

    //Bind data to combobox
    cb_Test.ItemsSource = _Line_Data;
}

Теперь поместите ComboBox в свой дизайн.Чтобы использовать его как обычный ComboBox, удалите DisplayMemberPath и переименуйте «ColorComboBoxItem» в «CustomComboBoxItem»:

<ComboBox x:Name="cb_Test" FontSize="36" Padding="1,0" MinWidth="100" MaxWidth="400" Margin="5,53,10,207" FontFamily="Calibri" Background="#FFBFBFBF" Foreground="#FF1F4E79" BorderBrush="#FF1F4E79" VerticalContentAlignment="Center" TabIndex="5" IsSynchronizedWithCurrentItem="False"
            Style="{DynamicResource RoundedComboBox}" 
            ItemContainerStyle="{DynamicResource ColorComboBoxItem}" 
            DisplayMemberPath="ITEM_SHORT_NAME" />

Теперь добавьте следующие стили / шаблон в App.xaml Application.Resources:

<!-- Rounded ComboBox Button -->
<Style x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="32" />
                    </Grid.ColumnDefinitions>
                    <Border
                    x:Name="Border"
                    Grid.ColumnSpan="2"
                    CornerRadius="8"
                    Background="{TemplateBinding Background}"
                    BorderBrush="#FF1F4E79"
                    BorderThickness="2" 
                />

                    <Path
                    x:Name="Arrow"
                    Grid.Column="1"    
                    Fill="{TemplateBinding Foreground}"
                    Stroke="{TemplateBinding Foreground}"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Data="M 0 0 L 4 4 L 8 0 Z"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
    <Border x:Name="PART_ContentHost" Focusable="True" />
</ControlTemplate>

<!-- ComboBox Template -->
<Style x:Key="RoundedComboBox" TargetType="{x:Type ComboBox}">
    <Setter Property="Foreground" Value="#333" />
    <Setter Property="BorderBrush" Value="Gray" />
    <Setter Property="Background" Value="White" />
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="FontSize" Value="13" />
    <Setter Property="MinWidth" Value="150"/>
    <Setter Property="MinHeight" Value="35"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ComboBox">
                <Grid>
                    <ToggleButton
                    Cursor="Hand"
                    Name="ToggleButton"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    Background="{TemplateBinding Background}"
                    Foreground="{TemplateBinding Foreground}"
                    Style="{StaticResource ComboBoxToggleButton}"
                    Grid.Column="2"
                    Focusable="false"
                    IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
                    ClickMode="Press"/>

                    <ContentPresenter
                    Name="ContentSite"
                    IsHitTestVisible="False"
                    Content="{TemplateBinding SelectionBoxItem}"
                    ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                    ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                    Margin="10,3,30,3"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Left" />
                    <TextBox x:Name="PART_EditableTextBox"
                    Style="{x:Null}"
                    Template="{StaticResource ComboBoxTextBox}"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Center"
                    Margin="3,3,23,3"
                    Focusable="True"                               
                    Visibility="Hidden"
                    IsReadOnly="{TemplateBinding IsReadOnly}"/>
                    <Popup
                    Name="Popup"
                    Placement="Bottom"
                    IsOpen="{TemplateBinding IsDropDownOpen}"
                    AllowsTransparency="True"
                    Focusable="False"
                    PopupAnimation="Slide">
                        <Grid
                        Name="DropDown"
                        SnapsToDevicePixels="True"               
                        MinWidth="{TemplateBinding ActualWidth}"
                        MaxHeight="{TemplateBinding MaxDropDownHeight}">
                            <Border
                            CornerRadius="10"
                            x:Name="DropDownBorder"
                            Background="#FFBFBFBF"
                            BorderThickness="2"
                            BorderBrush="#FF1F4E79"
                            />
                            <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
                                <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                            </ScrollViewer>
                        </Grid>
                    </Popup>

                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasItems" Value="false">
                        <Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
                    </Trigger>
                    <Trigger Property="IsGrouping" Value="true">
                        <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                    </Trigger>
                    <Trigger Property="IsEditable" Value="true">
                        <Setter Property="IsTabStop" Value="false"/>
                        <Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
                        <Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
    </Style.Triggers>
</Style>

<!--This style uses the normal items.add function-->
<Style x:Key="CustomComboBoxItem" TargetType="{x:Type ComboBoxItem}">
    <Setter Property="SnapsToDevicePixels" Value="true" />
    <Setter Property="HorizontalAlignment" Value="Stretch" />
    <Setter Property="VerticalAlignment" Value="Stretch" />
    <Setter Property="FontSize" Value="30" />
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ComboBoxItem">
                <Border
                Name="Border"
                Padding="5"
                Margin="2"
                BorderThickness="2,0,0,0"
                CornerRadius="0"
                Background="Transparent"
                BorderBrush="Transparent">
                    <TextBlock TextAlignment="Left">
                    <ContentPresenter />
                    </TextBlock>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsHighlighted" Value="true">
                        <Setter TargetName="Border" Property="BorderBrush" Value="#FF3737CB"/>
                        <Setter TargetName="Border" Property="Background" Value="#FF6ACDEA"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!--This style uses the structure to fill items and set the item color-->
<Style x:Key="ColorComboBoxItem" TargetType="{x:Type ComboBoxItem}">
    <Setter Property="SnapsToDevicePixels" Value="true" />
    <Setter Property="HorizontalAlignment" Value="Stretch" />
    <Setter Property="VerticalAlignment" Value="Stretch" />
    <Setter Property="FontSize" Value="30" />
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="Foreground" Value="{Binding ITEM_COLOR}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ComboBoxItem">
                <Border
                    Name="Border"
                    Padding="5"
                    Margin="2"
                    BorderThickness="2,0,0,0"
                    CornerRadius="0"
                    Background="Transparent"
                    BorderBrush="Transparent">
                    <TextBlock Text="{Binding ITEM_NAME}" TextAlignment="Left">
                    </TextBlock>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsHighlighted" Value="true">
                        <Setter TargetName="Border" Property="BorderBrush" Value="#FF3737CB"/>
                        <Setter TargetName="Border" Property="Background" Value="#FF6ACDEA"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Надеюсь, это поможет ..

0 голосов
/ 20 марта 2019

Это решение для WPF + MVVM.

Некоторые другие решения работают, а некоторые нет. Проблема с некоторыми другими решениями заключается в том, что если они не работают, иногда трудно отладить, почему он не работает, особенно если у вас нет опыта работы с WPF.

По моему мнению, предпочтительно использовать строки для привязок и преобразовывать их в перечисление в C #, что означает, что все проще устранить.

Возможно, вам потребуется использовать ReSharper, он автоматически предложит любые пропущенные пространства имен.

Создать перечисление с атрибутами описания:

public enum EnumSelectedView
{
    [Description("Drop Down 1")]
    DropDown1 = 0,

    [Description("Drop Down 2")]
    DropDown2 = 1,
}

И ComboBox:

<ComboBox HorizontalAlignment="Right"
   VerticalAlignment="Top"
   Width="130"
   ItemsSource="{Binding AvailableSelectedViews, Mode=OneWay}"
   SelectedItem="{Binding SelectedView, Mode=TwoWay, Converter={StaticResource enumToDescriptionConverter}}"
</ComboBox>

Конвертер в XAML должен указывать на класс C #. Если вы используете UserControl или Window, это будет UserControl.Resources или Window.Resources.

<DataTemplate.Resources>
    <converters:EnumToDescriptionConverter x:Key="enumToDescriptionConverter" />            
</DataTemplate.Resources>

Добавьте некоторые методы расширения и конвертер в любом месте вашего проекта:

using System;

namespace CMCMarkets.Phantom.CoreUI.Converters
{
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Windows.Data;

    public class EnumToDescriptionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if ((value is Enum) == false) throw new ArgumentException("Error: value is not an enum.");
            return ((Enum)value)?.GetDescriptionAttribute();
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if ((value is string) == false)
            {
                throw new ArgumentException("Error: Value is not a string");
            }
            foreach (var item in Enum.GetValues(targetType))
            {
                var asString = (item as Enum).GetDescriptionAttribute();
                if (asString == (string)value)
                {
                    return item;
                }
            }
            throw new ArgumentException("Error: Unable to match string to enum description.");
        }
    }

    public static class EnumExtensions
    {
        /// <summary>
        /// For a single enum entry, return the [Description("")] attribute.
        /// </summary>
        public static string GetDescriptionAttribute(this Enum enumObj)
        {
            FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());
            object[] attribArray = fieldInfo.GetCustomAttributes(false);
            if (attribArray.Length == 0)
            {
                return enumObj.ToString();
            }
            else
            {
                DescriptionAttribute attrib = attribArray[0] as DescriptionAttribute;
                return attrib?.Description;
            }
        }

        /// <summary>
        /// For an enum type, return a list of all possible [Description("")] attributes.
        /// </summary>
        /*
         * Example: List<string> descriptions = EnumExtensions.GetDescriptionAttributeList<MyEnumType>();
         */
        public static List<string> GetDescriptionAttributeList<T>()
        {
            return typeof(T).GetEnumValues().Cast<Enum>().Select(x => x.GetDescriptionAttribute()).ToList();
        }

        /// <summary>
        /// For an enum instance, return a list of all possible [Description("")] attributes.
        /// </summary>
        /*
         * Example:
         *
         * List<string> descriptions = typeof(CryptoExchangePricingOrGraphView).GetDescriptionAttributeList();
         */
        public static List<string> GetDescriptionAttributeList(this Type type)
        {
            return type.GetEnumValues().Cast<Enum>().Select(x => x.GetDescriptionAttribute()).ToList();
        }

        /// <summary>
        /// For an enum instance, return a list of all possible [Description("")] attributes.
        /// </summary>
        /*
         * Example:
         *
         * MyEnumType x;
         * List<string> descriptions = x.GetDescriptionAttributeList();
         */
        public static List<string> GetDescriptionAttributeList(this Enum thisEnum)
        {
            return thisEnum.GetType().GetEnumValues().Cast<Enum>().Select(x => x.GetDescriptionAttribute()).ToList();
        }
    }
}

В вашей ViewModel:

public IReadOnlyList<string> AvailableSelectedViews { get; }

А в конструкторе:

 this.AvailableSelectedViews = typeof(EnumSelectedView).GetDescriptionAttributeList();

Выбранный предмет будет привязан к этому. Он использует конвертер для перехода от строки в выпадающем списке прямо к списку. Вы также можете выполнить преобразование внутри средства обновления свойств, используя методы расширения, описанные выше.

public EnumSelectedView SelectedView { get; set; }
0 голосов
/ 16 марта 2015

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

Private Sub ComboBox*_Change()
Dim T As String
T = Left(ComboBox*.Text, 1)
TextBox*.Value = T
End Sub

(замените * соответствующими номерами). В результате при выборе раскрывающийся список будет отображать список какобычно, но текстовое поле, лежащее над ним, покажет только его первый символ.

Надеюсь, это поможет.

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