Привязка свойства перечисления к ComboBox в WPF

В качестве примера возьмем следующий код:

public enum ExampleEnum { FooBar, BarFoo }

public class ExampleClass : INotifyPropertyChanged
{
    private ExampleEnum example;

    public ExampleEnum ExampleProperty 
    { get { return example; } { /* set and notify */; } }
}

Я хочу привязать свойство ExampleProperty к ComboBox, чтобы оно отображало параметры «FooBar» и «BarFoo» и работало в режиме TwoWay. Оптимально я хочу, чтобы мое определение ComboBox выглядело примерно так:

<ComboBox ItemsSource = "What goes here?" SelectedItem = "{Binding Path=ExampleProperty}" />

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

Есть ли способ лучше или какой-то канонический? Вы бы обычно использовали конвертеры и как бы вы заполняли ComboBox правильными значениями? Я даже не хочу сейчас начинать работать с i18n.

Редактировать

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

Получить значения Enum в виде списка строк через ObjectDataProvider из статического метода Enum.GetValues:

<Window.Resources>
    <ObjectDataProvider MethodName = "GetValues"
        ObjectType = "{x:Type sys:Enum}"
        x:Key = "ExampleEnumValues">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName = "ExampleEnum" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

Это я могу использовать как ItemsSource для моего ComboBox:

<ComboBox ItemsSource = "{Binding Source = {StaticResource ExampleEnumValues}}"/>

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

ageektrapped 19.09.2008 04:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
262
1
181 982
14

Ответы 14

Я не знаю, возможно ли это только в XAML, но попробуйте следующее:

Дайте своему ComboBox имя, чтобы вы могли получить к нему доступ в коде позади: "typesComboBox1"

Теперь попробуйте следующее

typesComboBox1.ItemsSource = Enum.GetValues(typeof(ExampleEnum));

Попробуйте использовать

<ComboBox ItemsSource = "{Binding Source = {StaticResource ExampleEnumValues}}"
    SelectedValue = "{Binding Path=ExampleProperty}" />

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

Maximilian 12.09.2008 16:48

вы можете рассмотреть что-то вроде этого:

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

    <Style x:Key = "enumStyle" TargetType = "{x:Type TextBlock}">
        <Setter Property = "Text" Value = "&lt;NULL&gt;"/>
        <Style.Triggers>
            <Trigger Property = "Tag">
                <Trigger.Value>
                    <proj:YourEnum>Value1<proj:YourEnum>
                </Trigger.Value>
                <Setter Property = "Text" Value = "{DynamicResource yourFriendlyValue1}"/>
            </Trigger>
            <!-- add more triggers here to reflect your enum -->
        </Style.Triggers>
    </Style>
    
  2. определите свой стиль для ComboBoxItem

    <Style TargetType = "{x:Type ComboBoxItem}">
        <Setter Property = "ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <TextBlock Tag = "{Binding}" Style = "{StaticResource enumStyle}"/>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
  3. добавьте поле со списком и загрузите его значениями перечисления:

    <ComboBox SelectedValue = "{Binding Path=your property goes here}" SelectedValuePath = "Content">
        <ComboBox.Items>
            <ComboBoxItem>
                <proj:YourEnum>Value1</proj:YourEnum>
            </ComboBoxItem>
        </ComboBox.Items>
    </ComboBox>
    

если у вас большое перечисление, вы, конечно, можете сделать то же самое в коде, сэкономив много времени на вводе текста. Мне нравится этот подход, поскольку он упрощает локализацию - вы определяете все шаблоны один раз, а затем обновляете только файлы строковых ресурсов.

Здесь мне помог SelectedValuePath = "Content". У меня есть свои ComboBoxItems как строковые значения, и я не могу преобразовать ComboBoxItem в мой тип Enum. Спасибо

adriaanp 13.05.2009 07:43

Вы можете создать собственное расширение разметки.

Пример использования:

enum Status
{
    [Description("Available.")]
    Available,
    [Description("Not here right now.")]
    Away,
    [Description("I don't have time right now.")]
    Busy
}

В верхней части вашего XAML:

    xmlns:my = "clr-namespace:namespace_to_enumeration_extension_class

а потом...

<ComboBox 
    ItemsSource = "{Binding Source = {my:Enumeration {x:Type my:Status}}}" 
    DisplayMemberPath = "Description" 
    SelectedValue = "{Binding CurrentStatus}"  
    SelectedValuePath = "Value"  /> 

А реализация ...

public class EnumerationExtension : MarkupExtension
  {
    private Type _enumType;


    public EnumerationExtension(Type enumType)
    {
      if (enumType == null)
        throw new ArgumentNullException("enumType");

      EnumType = enumType;
    }

    public Type EnumType
    {
      get { return _enumType; }
      private set
      {
        if (_enumType == value)
          return;

        var enumType = Nullable.GetUnderlyingType(value) ?? value;

        if (enumType.IsEnum == false)
          throw new ArgumentException("Type must be an Enum.");

        _enumType = value;
      }
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      var enumValues = Enum.GetValues(EnumType);

      return (
        from object enumValue in enumValues
        select new EnumerationMember{
          Value = enumValue,
          Description = GetDescription(enumValue)
        }).ToArray();
    }

    private string GetDescription(object enumValue)
    {
      var descriptionAttribute = EnumType
        .GetField(enumValue.ToString())
        .GetCustomAttributes(typeof (DescriptionAttribute), false)
        .FirstOrDefault() as DescriptionAttribute;


      return descriptionAttribute != null
        ? descriptionAttribute.Description
        : enumValue.ToString();
    }

    public class EnumerationMember
    {
      public string Description { get; set; }
      public object Value { get; set; }
    }
  }

@ Грегор С. Что у меня: Перечисление?

joshua 10.07.2012 10:53

@Crown 'my' - это префикс пространства имен, который вы объявляете в верхней части файла xaml: например, xmlns: my = "clr-namespace: namespace_to_enumeration_extension_c‌ lass. Перечисление является сокращением от EnumerationExtension, в xaml вам не нужно писать полное расширение название класса.

Gregor Slavec 10.07.2012 11:20

Можно ли, используя приведенный выше код, сохранить значение выбранного элемента в пользовательских настройках и прочитать его?

papaiatis 23.07.2012 11:42

Да, просто установите / получите свойство CurrentStatus в вашей модели просмотра.

Gregor Slavec 23.07.2012 14:40

+1, но количество кода, требуемого WPF для выполнения простейших задач, действительно потрясающе.

Konrad Morawski 29.08.2012 18:36

Мне не очень нравится, как он заставляет вас использовать ссылку на часть вашей модели - тип перечисления - в представлении в параметре ItemsSource. Чтобы сохранить представление и модель разделенными, мне нужно было бы создать копию перечисления в ViewModel и код ViewModel для перевода между ними ... Что сделало бы решение не таким простым. Или есть способ предоставить сам тип из ViewModel?

lampak 31.08.2012 20:01

Еще одно ограничение заключается в том, что вы не можете этого сделать, если у вас несколько языков.

River-Claire Williamson 23.01.2013 00:16

Если вы столкнулись с ошибкой "EnumerationExtension” does not include a constructor that has the specified number of arguments, попробуйте использовать ItemsSource = "{Binding Source = {my:Enumeration EnumType = {x:Type my:Status}}}"

Markus Weber 30.03.2016 17:12

Не могу получить эту часть пространства имен ... Все выделяется как «не существует».

C4d 31.08.2016 18:04

@RiverWilliamson ваш атрибут может быть чувствительным к культуре, так что вы можете указать имя и тип ресурса

Davi Fiamenghi 12.12.2016 21:24

Я сталкивался с этой проблемой несколько раз на протяжении многих лет, и использование вашего MarkupExtension обеспечивает самый чистый механизм, который я видел, не только для автоматического использования прокси-класса вместо значения перечисления, но и для его локализации. Я абстрагировал бит, который выполняет отражение, в помощник, который можно использовать в коде, и теперь все мое описание перечисления (для XAML или отчетов) проходит через один класс, а затем используется MarkupExtension, который у вас есть. Хорошая работа!

outbred 16.12.2016 01:20

У меня была большая проблема с отображением описания, затем я переопределил ToString в классе EnumerationMember, чтобы вернуть описание, и теперь он работает как пуля 7,62 мм.

Felype 09.02.2017 19:56

Вы можете показать, где находится свойство CurrentStatus? Это в файле xaml.cs? Мне не удается привязать к SelectedValue

vbp13 24.01.2019 00:02

Я также пытаюсь выяснить, где находится «CurrentStatus», потому что у меня есть атрибут Description с числами и буквами (1120S), который не будет правильно назначать значение при его выборе. Он правильно отображается в раскрывающемся списке, но при выборе он не присваивает значение моей модели просмотра.

HeedfulCrayon 06.03.2019 19:28

В модели просмотра вы можете иметь:

public MyEnumType SelectedMyEnumType 
{
    get { return _selectedMyEnumType; }
    set { 
            _selectedMyEnumType = value;
            OnPropertyChanged("SelectedMyEnumType");
        }
}

public IEnumerable<MyEnumType> MyEnumTypeValues
{
    get
    {
        return Enum.GetValues(typeof(MyEnumType))
            .Cast<MyEnumType>();
    }
}

В XAML ItemSource связывается с MyEnumTypeValues, а SelectedItem - с SelectedMyEnumType.

<ComboBox SelectedItem = "{Binding SelectedMyEnumType}" ItemsSource = "{Binding MyEnumTypeValues}"></ComboBox>

Это прекрасно работало в моем универсальном приложении, и его было очень легко реализовать. Спасибо!

Nathan Strutz 10.11.2017 00:03

Это отлично работает и требует гораздо меньше кода, чем выбранный ответ.

j__ 25.02.2021 18:08

На основе принятого, но теперь удаленного ответа, предоставленного Ageektrapped, я создал уменьшенную версию без некоторых более продвинутых функций. Весь код включен здесь, чтобы вы могли копировать и вставлять его и не блокировались ссылочной гнилью.

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

public enum ExampleEnum {

  [Description("Foo Bar")]
  FooBar,

  [Description("Bar Foo")]
  BarFoo

}

Вот класс, используемый в качестве источника элементов:

public class EnumItemsSource : Collection<String>, IValueConverter {

  Type type;

  IDictionary<Object, Object> valueToNameMap;

  IDictionary<Object, Object> nameToValueMap;

  public Type Type {
    get { return this.type; }
    set {
      if (!value.IsEnum)
        throw new ArgumentException("Type is not an enum.", "value");
      this.type = value;
      Initialize();
    }
  }

  public Object Convert(Object value, Type targetType, Object parameter, CultureInfo culture) {
    return this.valueToNameMap[value];
  }

  public Object ConvertBack(Object value, Type targetType, Object parameter, CultureInfo culture) {
    return this.nameToValueMap[value];
  }

  void Initialize() {
    this.valueToNameMap = this.type
      .GetFields(BindingFlags.Static | BindingFlags.Public)
      .ToDictionary(fi => fi.GetValue(null), GetDescription);
    this.nameToValueMap = this.valueToNameMap
      .ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    Clear();
    foreach (String name in this.nameToValueMap.Keys)
      Add(name);
  }

  static Object GetDescription(FieldInfo fieldInfo) {
    var descriptionAttribute =
      (DescriptionAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(DescriptionAttribute));
    return descriptionAttribute != null ? descriptionAttribute.Description : fieldInfo.Name;
  }

}

Вы можете использовать его в XAML следующим образом:

<Windows.Resources>
  <local:EnumItemsSource
    x:Key = "ExampleEnumItemsSource"
    Type = "{x:Type local:ExampleEnum}"/>
</Windows.Resources>
<ComboBox
  ItemsSource = "{StaticResource ExampleEnumItemsSource}"
  SelectedValue = "{Binding ExampleProperty, Converter = {StaticResource ExampleEnumItemsSource}}"/> 

Я предпочитаю не использовать имя перечисления в пользовательском интерфейсе. Я предпочитаю использовать другое значение для пользователя (DisplayMemberPath) и другое значение (в данном случае enum) (SelectedValuePath). Эти два значения могут быть упакованы в KeyValuePair и сохранены в словаре.

XAML

<ComboBox Name = "fooBarComboBox" 
          ItemsSource = "{Binding Path=ExampleEnumsWithCaptions}" 
          DisplayMemberPath = "Value" 
          SelectedValuePath = "Key"
          SelectedValue = "{Binding Path=ExampleProperty, Mode=TwoWay}" > 

C#

public Dictionary<ExampleEnum, string> ExampleEnumsWithCaptions { get; } =
    new Dictionary<ExampleEnum, string>()
    {
        {ExampleEnum.FooBar, "Foo Bar"},
        {ExampleEnum.BarFoo, "Reversed Foo Bar"},
        //{ExampleEnum.None, "Hidden in UI"},
    };


private ExampleEnum example;
public ExampleEnum ExampleProperty
{
    get { return example; }
    set { /* set and notify */; }
}

Обновлено: совместим с шаблоном MVVM.

Я думаю, что ваш ответ недооценен, это лучший вариант, учитывая то, чего ожидает сам ComboBox. Возможно, вы могли бы поместить построитель словаря в получатель, используя Enum.GetValues, но это не решило бы часть отображаемых имен. В конце концов, особенно если реализован I18n, вам все равно придется вручную изменить что-то, если перечисление изменится. Но перечисления не должны часто меняться, если они вообще меняются? +1

heltonbiker 14.10.2013 18:21

Позвольте мне исправить это, как вы написали в комментарии: private static readonly Dictionary<ExampleEnum, string> EnumMapping = new Dictionary<ExampleEnum, string>() { {ExampleEnum.FooBar, "Foo Bar"}, {ExampleEnum.BarFoo, "Reversed Foo Bar"}, //{ExampleEnum.None, "Hidden in UI"}, }; public Dictionary<ExampleEnum, string> ExampleEnumsWithCaptions { get { return EnumMapping; } } :)

Pragmateek 05.08.2014 19:04

Этот ответ потрясающий, и он позволяет локализовать описания перечислений ... Спасибо за это!

Shay 04.05.2017 11:28

Это решение очень хорошее, потому что оно обрабатывает как перечисление, так и локализацию с меньшим количеством кода, чем другие решения!

hfann 23.06.2017 01:20

Проблема со словарем состоит в том, что ключи упорядочены по хеш-значению, поэтому контроль над этим ограничен. Хотя я был немного более подробным, вместо этого я использовал List <KeyValuePair <enum, string >>. Хорошая идея.

Kevin Brock 30.03.2018 17:38

@CoperNick @Pragmateek новое исправление: public Dictionary<ExampleEnum, string> ExampleEnumsWithCaptions { get; } = new Dictionary<ExampleEnum, string>() { {ExampleEnum.FooBar, "Foo Bar"}, {ExampleEnum.BarFoo, "Reversed Foo Bar"}, //{ExampleEnum.None, "Hidden in UI"}, };

Jinjinov 15.11.2018 16:37

@JinJi Обновлено до C# 6. Ваше исправление делает код короче. Спасибо.

CoperNick 15.11.2018 20:54

Я использовала этот узор, мне нравится его краткость и изящество. Лично я поменял местами Key и Value, потому что в моей голове словарь будет содержать строковые ключи для значений перечисления. Но я думаю, это не имеет значения.

LWChris 04.02.2020 14:38

Вот общее решение с использованием вспомогательного метода. Это также может обрабатывать перечисление любого базового типа (byte, sbyte, uint, long и т. д.)

Вспомогательный метод:

static IEnumerable<object> GetEnum<T>() {
    var type    = typeof(T);
    var names   = Enum.GetNames(type);
    var values  = Enum.GetValues(type);
    var pairs   =
        Enumerable.Range(0, names.Length)
        .Select(i => new {
                Name    = names.GetValue(i)
            ,   Value   = values.GetValue(i) })
        .OrderBy(pair => pair.Name);
    return pairs;
}//method

Просмотреть модель:

public IEnumerable<object> EnumSearchTypes {
    get {
        return GetEnum<SearchTypes>();
    }
}//property

Поле со списком:

<ComboBox
    SelectedValue        = "{Binding SearchType}"
    ItemsSource          = "{Binding EnumSearchTypes}"
    DisplayMemberPath    = "Name"
    SelectedValuePath    = "Value"
/>

Используйте ObjectDataProvider:

<ObjectDataProvider x:Key = "enumValues"
   MethodName = "GetValues" ObjectType = "{x:Type System:Enum}">
      <ObjectDataProvider.MethodParameters>
           <x:Type TypeName = "local:ExampleEnum"/>
      </ObjectDataProvider.MethodParameters>
 </ObjectDataProvider>

а затем привязать к статическому ресурсу:

ItemsSource = "{Binding Source = {StaticResource enumValues}}"

Найдите этот решение в этом блоге

Хороший ответ. Между прочим, это избавляет вас от необходимости беспокоиться о Converter для проблемы с enum-to-string.

DonBoitnott 21.11.2017 19:57

Связанное решение кажется мертвым (корейский или японский текст?). Если я помещаю ваш код в свои ресурсы XAML, он говорит, что Enum не поддерживается в проекте WPF.

Sebastian 14.06.2018 12:28

Вам нужно добавить 'xmlns: System = "clr-namespace: System; assembly = mscorlib"' в определения xlmns тега Window

yoel halb 13.01.2021 05:53

Это конкретный ответ DevExpress, основанный на ответе Gregor S., получившем наибольшее количество голосов (в настоящее время у него 128 голосов).

Это означает, что мы можем сохранить единообразие стиля во всем приложении:

К сожалению, исходный ответ не работает с ComboBoxEdit от DevExpress без некоторых модификаций.

Во-первых, XAML для ComboBoxEdit:

<dxe:ComboBoxEdit ItemsSource = "{Binding Source = {xamlExtensions:XamlExtensionEnumDropdown {x:myEnum:EnumFilter}}}"
    SelectedItem = "{Binding BrokerOrderBookingFilterSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    DisplayMember = "Description"
    MinWidth = "144" Margin = "5" 
    HorizontalAlignment = "Left"
    IsTextEditable = "False"
    ValidateOnTextInput = "False"
    AutoComplete = "False"
    IncrementalFiltering = "True"
    FilterCondition = "Like"
    ImmediatePopup = "True"/>

Излишне говорить, что вам нужно будет указать xamlExtensions в пространстве имен, которое содержит класс расширения XAML (который определен ниже):

xmlns:xamlExtensions = "clr-namespace:XamlExtensions"

И мы должны указать myEnum на пространство имен, содержащее перечисление:

xmlns:myEnum = "clr-namespace:MyNamespace"

Затем перечисление:

namespace MyNamespace
{
    public enum EnumFilter
    {
        [Description("Free as a bird")]
        Free = 0,

        [Description("I'm Somewhat Busy")]
        SomewhatBusy = 1,

        [Description("I'm Really Busy")]
        ReallyBusy = 2
    }
}

Проблема с XAML заключается в том, что мы не можем использовать SelectedItemValue, так как это вызывает ошибку, поскольку сеттер недоступен (немного недосмотра с вашей стороны, DevExpress). Поэтому нам нужно изменить наш ViewModel, чтобы получить значение непосредственно из объекта:

private EnumFilter _filterSelected = EnumFilter.All;
public object FilterSelected
{
    get
    {
        return (EnumFilter)_filterSelected;
    }
    set
    {
        var x = (XamlExtensionEnumDropdown.EnumerationMember)value;
        if (x != null)
        {
            _filterSelected = (EnumFilter)x.Value;
        }
        OnPropertyChanged("FilterSelected");
    }
}

Для полноты, вот расширение XAML из исходного ответа (слегка переименовано):

namespace XamlExtensions
{
    /// <summary>
    ///     Intent: XAML markup extension to add support for enums into any dropdown box, see http://bit.ly/1g70oJy. We can name the items in the
    ///     dropdown box by using the [Description] attribute on the enum values.
    /// </summary>
    public class XamlExtensionEnumDropdown : MarkupExtension
    {
        private Type _enumType;


        public XamlExtensionEnumDropdown(Type enumType)
        {
            if (enumType == null)
            {
                throw new ArgumentNullException("enumType");
            }

            EnumType = enumType;
        }

        public Type EnumType
        {
            get { return _enumType; }
            private set
            {
                if (_enumType == value)
                {
                    return;
                }

                var enumType = Nullable.GetUnderlyingType(value) ?? value;

                if (enumType.IsEnum == false)
                {
                    throw new ArgumentException("Type must be an Enum.");
                }

                _enumType = value;
            }
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            var enumValues = Enum.GetValues(EnumType);

            return (
                from object enumValue in enumValues
                select new EnumerationMember
                       {
                           Value = enumValue,
                           Description = GetDescription(enumValue)
                       }).ToArray();
        }

        private string GetDescription(object enumValue)
        {
            var descriptionAttribute = EnumType
                .GetField(enumValue.ToString())
                .GetCustomAttributes(typeof (DescriptionAttribute), false)
                .FirstOrDefault() as DescriptionAttribute;


            return descriptionAttribute != null
                ? descriptionAttribute.Description
                : enumValue.ToString();
        }

        #region Nested type: EnumerationMember
        public class EnumerationMember
        {
            public string Description { get; set; }
            public object Value { get; set; }
        }
        #endregion
    }
}

Отказ от ответственности: я не имею отношения к DevExpress. Telerik - также отличная библиотека.

Для справки, я не связан с DevExpress. У Telerik также есть прекрасные библиотеки, и этот метод может даже не понадобиться для их библиотеки.

Contango 30.06.2015 18:53

Я создал проект CodePlex с открытым исходным кодом, который делает это. Вы можете загрузить пакет NuGet с сайта здесь.

<enumComboBox:EnumComboBox EnumType = "{x:Type demoApplication:Status}" SelectedValue = "{Binding Status}" />

Если вы используете MVVM, основываясь на ответе @rudigrobler, вы можете сделать следующее:

Добавьте следующее свойство в класс ViewModel

public Array ExampleEnumValues => Enum.GetValues(typeof(ExampleEnum));

Затем в XAML сделайте следующее:

<ComboBox ItemsSource = "{Binding ExampleEnumValues}" ... />

Мой любимый способ сделать это - использовать ValueConverter, чтобы ItemsSource и SelectedValue были привязаны к одному и тому же свойству. Для этого требуется без дополнительных свойств, чтобы ваша ViewModel оставалась красивой и чистой.

<ComboBox ItemsSource = "{Binding Path=ExampleProperty, Converter = {x:EnumToCollectionConverter}, Mode=OneTime}"
          SelectedValuePath = "Value"
          DisplayMemberPath = "Description"
          SelectedValue = "{Binding Path=ExampleProperty}" />

И определение Конвертера:

public static class EnumHelper
{
  public static string Description(this Enum e)
  {
    return (e.GetType()
             .GetField(e.ToString())
             .GetCustomAttributes(typeof(DescriptionAttribute), false)
             .FirstOrDefault() as DescriptionAttribute)?.Description ?? e.ToString();
  }
}

[ValueConversion(typeof(Enum), typeof(IEnumerable<ValueDescription>))]
public class EnumToCollectionConverter : MarkupExtension, IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return Enum.GetValues(value.GetType())
               .Cast<Enum>()
               .Select(e => new ValueDescription() { Value = e, Description = e.Description()})
               .ToList();
  }
  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return null;
  }
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    return this;
  }
}

Этот конвертер будет работать с любым перечислением. ValueDescription - это простой класс со свойством Value и свойством Description. Вы можете так же легко использовать Tuple с Item1 и Item2 или KeyValuePair с Key и Value вместо Value и Description или любой другой класс по вашему выбору, если он может содержать значение перечисления и строковое описание этого значения перечисления.

Хороший ответ! Для класса ValueDescription свойство Description может быть опущено, если оно не требуется. Также работает простой класс со свойством только Value!

pogosama 13.10.2017 16:06

Кроме того, если вы хотите выполнить привязку к RadioButton, тогда метод Convert должен возвращать список строк, то есть .Select(e => e.ToString()), вместо использования класса ValueDescription.

pogosama 13.10.2017 16:52

Вместо ValueDescription также можно использовать KeyValuePair, например показано здесь

Apfelkuacha 26.12.2019 23:32

Код

    public enum RULE
    {
        [Description( "Любые, без ограничений" )]
        any,
        [Description( "Любые если будет три в ряд" )]
        anyThree,
        [Description( "Соседние, без ограничений" )]
        nearAny,
        [Description( "Соседние если будет три в ряд" )]
        nearThree
    }

    class ExtendRULE
    {
        public static object Values
        {
            get
            {
                List<object> list = new List<object>();
                foreach( RULE rule in Enum.GetValues( typeof( RULE ) ) )
                {
                    string desc = rule.GetType().GetMember( rule.ToString() )[0].GetCustomAttribute<DescriptionAttribute>().Description;
                    list.Add( new { value = rule, desc = desc } );
                }
                return list;
            }
        }
    }

XAML

<StackPanel>
   <ListBox ItemsSource= "{Binding Source = {x:Static model:ExtendRULE.Values}}" DisplayMemberPath = "desc" SelectedValuePath = "value" SelectedValue = "{Binding SelectedRule}"/>
   <ComboBox ItemsSource = "{Binding Source = {x:Static model:ExtendRULE.Values}}" DisplayMemberPath = "desc" SelectedValuePath = "value" SelectedValue = "{Binding SelectedRule}"/>                        
</StackPanel>

Другие вопросы по теме