Как привязать RadioButtons к перечислению?

406

У меня есть перечисление как это:

public enum MyLovelyEnum
{
    FirstSelection,
    TheOtherSelection,
    YetAnotherOne
};

Я получил свойство в моем DataContext:

public MyLovelyEnum VeryLovelyEnum { get; set; }

И я получил три RadioButton в моем клиенте WPF.

<RadioButton Margin="3">First Selection</RadioButton>
<RadioButton Margin="3">The Other Selection</RadioButton>
<RadioButton Margin="3">Yet Another one</RadioButton>

Теперь, как мне привязать RadioButtons к свойству для правильной двусторонней привязки?

Сэм
источник
3
Если вы хотите сделать это без указания отдельных RadioButton-ов в вашем XAML, я бы порекомендовал ListBox, привязанный к значениям перечисления, подобным этому или этому , и для которого шаблон элемента был перезаписан для использования RadioButtons, как этот .
Рэйчел

Ответы:

389

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

public class EnumBooleanConverter : IValueConverter
{
  #region IValueConverter Members
  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    string parameterString = parameter as string;
    if (parameterString == null)
      return DependencyProperty.UnsetValue;

    if (Enum.IsDefined(value.GetType(), value) == false)
      return DependencyProperty.UnsetValue;

    object parameterValue = Enum.Parse(value.GetType(), parameterString);

    return parameterValue.Equals(value);
  }

  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    string parameterString = parameter as string;
    if (parameterString == null)
        return DependencyProperty.UnsetValue;

    return Enum.Parse(targetType, parameterString);
  }
  #endregion
}

И в XAML-части вы используете:

<Grid>
    <Grid.Resources>
      <l:EnumBooleanConverter x:Key="enumBooleanConverter" />
    </Grid.Resources>
    <StackPanel >
      <RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=FirstSelection}">first selection</RadioButton>
      <RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=TheOtherSelection}">the other selection</RadioButton>
      <RadioButton IsChecked="{Binding Path=VeryLovelyEnum, Converter={StaticResource enumBooleanConverter}, ConverterParameter=YetAnotherOne}">yet another one</RadioButton>
    </StackPanel>
</Grid>
Lars
источник
51
Работал как шарм для меня. В качестве дополнения я изменил ConvertBack, чтобы он также возвращал UnsetValue на «false», потому что silverlight (и, по-видимому, собственно WPF) вызывает преобразователь дважды - один раз при сбросе старого значения переключателя и еще раз для установки нового. Я вешал другие вещи от установщика собственности, поэтому хотел, чтобы он вызывался только один раз. - if (parameterString == null || value.Equals (false)) return DependencyProperty.UnsetValue;
MarcE
8
Из того, что я могу сказать, это должно быть сделано, если переключатели не находятся в разных группах (и кнопки AFAIK без установленного GroupName, имеющие одного и того же родителя, по умолчанию находятся в одной группе). В противном случае вызовы устанавливают свойство «bounce» и приводят к странному поведению.
nlawalker
2
да, но если вы вызываете Unset в конвертере при установке на false, тогда это не истинный EnumToBooleanConverter, а скорее EnumToRadioButtonConverter. Поэтому вместо этого я проверяю, отличается ли значение в моём установщике свойств: if (_myEnumBackingField == value) return;
Стефан
8
Привязка к этому решению работает только в одну сторону. Я не мог программно переключить переключатель, назначив свойству binded другое значение. Если вы хотите правильное рабочее И лучшее решение, используйте подход Скотта.
l46kok
2
@Marc, разве не следует возвращать в этом случае «Binding.DoNothing», а не «DependencyProperty.UnsetValue»?
Марк А. Донохо
560

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

ConverterParameter = {x: Статический локальный: YourEnumType.Enum1}

<StackPanel>
    <StackPanel.Resources>          
        <local:ComparisonConverter x:Key="ComparisonConverter" />          
    </StackPanel.Resources>
    <RadioButton IsChecked="{Binding Path=YourEnumProperty, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:YourEnumType.Enum1}}" />
    <RadioButton IsChecked="{Binding Path=YourEnumProperty, Converter={StaticResource ComparisonConverter}, ConverterParameter={x:Static local:YourEnumType.Enum2}}" />
</StackPanel>

Тогда упростим конвертер:

public class ComparisonConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value?.Equals(parameter);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value?.Equals(true) == true ? parameter : Binding.DoNothing;
    }
}

Изменить (16 декабря '10):

Спасибо anon за предложение вернуть Binding.DoNothing, а не DependencyProperty.UnsetValue.


Примечание. Несколько групп радиокнопок в одном контейнере (17 февраля 2011 г.):

В xaml, если переключатели совместно используют один и тот же родительский контейнер, выбор одного из них отменяет выбор всех других в этом контейнере (даже если они связаны с другим свойством). Поэтому постарайтесь сохранить ваши RadioButton, связанные с общим свойством, сгруппированными в их собственном контейнере, как панель стека. В тех случаях, когда ваши связанные RadioButtons не могут совместно использовать один родительский контейнер, установите для свойства GroupName каждого RadioButton общее значение, чтобы логически сгруппировать их.


Изменить (5 апреля '11):

Упрощенное использование ConvertBack if-else для использования тернарного оператора.


Примечание. Тип перечисления, вложенный в класс (28 апреля 2011 г.):

Если ваш тип перечисления вложен в класс (а не непосредственно в пространство имен), вы можете использовать синтаксис «+» для доступа к перечислению в XAML, как указано в (не отмеченном) ответе на вопрос « Невозможно найти» Тип enum для статической ссылки в WPF :

ConverterParameter = {x: Статический локальный: YourClass + YourNestedEnumType.Enum1}

Однако из-за этой проблемы с Microsoft Connect дизайнер в VS2010 больше не будет загружать отчеты "Type 'local:YourClass+YourNestedEnumType' was not found.", но проект компилируется и успешно выполняется. Конечно, вы можете избежать этой проблемы, если сможете напрямую переместить тип enum в пространство имен.


Изменить (27 января '12):

Если использовать флаги Enum, конвертер будет выглядеть следующим образом:

public class EnumToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ((Enum)value).HasFlag((Enum)parameter);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value.Equals(true) ? parameter : Binding.DoNothing;
    }
}

Изменить (7 мая '15):

В случае Nullable Enum (это не задается в вопросе, но может быть необходимо в некоторых случаях, например, ORM возвращает ноль из БД или всякий раз, когда может иметь смысл, что в логике программы значение не указано), не забудьте добавить начальная нулевая проверка в методе Convert и возвращение соответствующего значения bool, которое, как правило, ложно (если вы не хотите, чтобы какая-либо радиокнопка была выбрана), как показано ниже:

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null) {
            return false; // or return parameter.Equals(YourEnumType.SomeDefaultValue);
        }
        return value.Equals(parameter);
    }

Примечание - NullReferenceException (10 октября '18):

Обновлен пример, чтобы исключить возможность создания исключения NullReferenceException. IsCheckedэто обнуляемый тип, поэтому возвращение Nullable<Boolean>кажется разумным решением.

Скотт
источник
26
Я согласен, я считаю, что это лучшее решение. Кроме того, использование этого преобразования приведет к разрыву проекта во время компиляции, а не во время выполнения, если значения перечисления изменены, что является большим преимуществом.
CrimsonX
4
Это, безусловно, гораздо лучшее решение, чем принятое. +1
OrPaz
7
Хорошее решение. Я бы добавил, что это действительно просто сравнительный преобразователь, сравнивающий 2 значения. У него может быть более общее имя, чем у EnumToBooleanConverter, например, ComparisonConverter
MikeKulls,
5
@ Скотт, Очень мило. Этот конвертер хорош в любых случаях, с атрибутом Flags или без него. Но в большинстве случаев было бы глупо использовать этот фильтр напрямую как конвертер с enum в качестве флагов. Причина в том, что вы должны получить логическое вычисление (| = или ^ =) с предыдущим значением, чтобы получить правильный результат, но конвертер не имеет доступа к предыдущему значению. Затем вы должны добавить bool для каждого значения перечисления и выполнить правильный логический расчет самостоятельно в своей модели MVVM. Но спасибо за каждую информацию, очень полезно.
Эрик Оуэлл
2
В Windows Phone 8 (возможно, в случае приложений Win Store) у нас нет x: static, поэтому мы не можем напрямую использовать решение здесь. Однако IDE / Complier достаточно умен и ищет строку по всем строковым литералам (в любом случае, я думаю). например, это работает <RadioButton IsChecked = "{Binding TrackingMode, ConverterParameter = Driving, Converter = {StaticResource EnumToBooleanConverter}, Mode = TwoWay}" /> Любые опечатки в Driving будут обнаружены во время проектирования / компиляции, а не во время выполнения.
Адарша
26

Для ответа EnumToBooleanConverter: вместо возврата DependencyProperty.UnsetValue рассмотрите возможность возврата Binding.DoNothing для случая, когда значение переключателя IsChecked становится ложным. Первый указывает на проблему (и может показывать пользователю красный прямоугольник или аналогичные индикаторы проверки), а второй просто указывает, что ничего не должно быть сделано, что и требуется в этом случае.

http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.convertback.aspx http://msdn.microsoft.com/en-us/library/system.windows.data.binding .donothing.aspx

скоро
источник
В Silverlight нет привязки. Ничего. Это только WPF. Вместо этого используйте ноль.
Александр Васильев
1
Привязка. Ничего не пропало из UWP.
BlackICE
5

Я бы использовал RadioButtons в ListBox, а затем привязать к SelectedValue.

Это старая ветка на эту тему, но основная идея должна быть такой же: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f-8d99-fecf45522395/

Мартин Мозер
источник
Я получаю двустороннее связывание, используя аналогичный метод, используя ListBox и DataTemplate, так что вы должны.
Брайан Андерсон
Эта ошибка: geekswithblogs.net/claraoscura/archive/2008/10/17/125901.aspx испортила мне день.
Slampen
3
На сегодняшний день это лучшее решение, все остальное вызывает избыточный код. ( Еще один пример использования ListBox)
HB
3

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

Пример 1

Действительно как для WPF, так и для UWP.

<MyControl>
    <MyControl.MyProperty>
        <Binding Converter="{StaticResource EnumToBooleanConverter}" Path="AnotherProperty">
            <Binding.ConverterParameter>
                <MyLibrary:MyEnum>Field</MyLibrary:MyEnum>
            </Binding.ConverterParameter>
        </MyControl>
    </MyControl.MyProperty>
</MyControl>

Пример 2

Действительно как для WPF, так и для UWP.

...
<MyLibrary:MyEnum x:Key="MyEnumField">Field</MyLibrary:MyEnum>
...

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={StaticResource MyEnumField}}"/>

Пример 3

Действительно только для WPF!

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static MyLibrary:MyEnum.Field}}"/>

UWP не поддерживает x:Static поэтому пример 3 исключен; при условии, что вы перейдете к примеру 1 , результатом будет более подробный код. Пример 2 немного лучше, но все же не идеал.

Решение

public abstract class EnumToBooleanConverter<TEnum> : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var Parameter = parameter as string;

        if (Parameter == null)
            return DependencyProperty.UnsetValue;

        if (Enum.IsDefined(typeof(TEnum), value) == false)
            return DependencyProperty.UnsetValue;

        return Enum.Parse(typeof(TEnum), Parameter).Equals(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        var Parameter = parameter as string;
        return Parameter == null ? DependencyProperty.UnsetValue : Enum.Parse(typeof(TEnum), Parameter);
    }
}

Затем для каждого типа, который вы хотите поддерживать, определите конвертер, который включает тип enum.

public class MyEnumToBooleanConverter : EnumToBooleanConverter<MyEnum>
{
    //Nothing to do!
}

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

Использование напоминает пример 2 , но на самом деле оно менее многословно.

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource MyEnumToBooleanConverter}, ConverterParameter=Field}"/>

Недостатком является то, что вы должны определить конвертер для каждого типа, который вы хотите поддерживать.

Джеймс М
источник
1

Я создал новый класс для обработки привязки RadioButtons и CheckBoxes к перечислениям. Он работает для помеченных перечислений (с несколькими вариантами флажков) и непомеченных перечислений для флажков с одним выбором или переключателей. Это также не требует никаких ValueConverters вообще.

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

public class EnumSelection<T> : INotifyPropertyChanged where T : struct, IComparable, IFormattable, IConvertible
{
  private T value; // stored value of the Enum
  private bool isFlagged; // Enum uses flags?
  private bool canDeselect; // Can be deselected? (Radio buttons cannot deselect, checkboxes can)
  private T blankValue; // what is considered the "blank" value if it can be deselected?

  public EnumSelection(T value) : this(value, false, default(T)) { }
  public EnumSelection(T value, bool canDeselect) : this(value, canDeselect, default(T)) { }
  public EnumSelection(T value, T blankValue) : this(value, true, blankValue) { }
  public EnumSelection(T value, bool canDeselect, T blankValue)
  {
    if (!typeof(T).IsEnum) throw new ArgumentException($"{nameof(T)} must be an enum type"); // I really wish there was a way to constrain generic types to enums...
    isFlagged = typeof(T).IsDefined(typeof(FlagsAttribute), false);

    this.value = value;
    this.canDeselect = canDeselect;
    this.blankValue = blankValue;
  }

  public T Value
  {
    get { return value; }
    set 
    {
      if (this.value.Equals(value)) return;
      this.value = value;
      OnPropertyChanged();
      OnPropertyChanged("Item[]"); // Notify that the indexer property has changed
    }
  }

  [IndexerName("Item")]
  public bool this[T key]
  {
    get
    {
      int iKey = (int)(object)key;
      return isFlagged ? ((int)(object)value & iKey) == iKey : value.Equals(key);
    }
    set
    {
      if (isFlagged)
      {
        int iValue = (int)(object)this.value;
        int iKey = (int)(object)key;

        if (((iValue & iKey) == iKey) == value) return;

        if (value)
          Value = (T)(object)(iValue | iKey);
        else
          Value = (T)(object)(iValue & ~iKey);
      }
      else
      {
        if (this.value.Equals(key) == value) return;
        if (!value && !canDeselect) return;

        Value = value ? key : blankValue;
      }
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void OnPropertyChanged([CallerMemberName] string propertyName = "")
  {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

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

public enum StartTask
{
  Manual,
  Automatic
}

[Flags()]
public enum DayOfWeek
{
  Sunday = 1 << 0,
  Monday = 1 << 1,
  Tuesday = 1 << 2,
  Wednesday = 1 << 3,
  Thursday = 1 << 4,
  Friday = 1 << 5,
  Saturday = 1 << 6
}

public enum AdditionalOptions
{
  None = 0,
  OptionA,
  OptionB
}

Теперь, как легко использовать этот класс:

public class MyViewModel : ViewModelBase
{
  public MyViewModel()
  {
    StartUp = new EnumSelection<StartTask>(StartTask.Manual);
    Days = new EnumSelection<DayOfWeek>(default(DayOfWeek));
    Options = new EnumSelection<AdditionalOptions>(AdditionalOptions.None, true, AdditionalOptions.None);
  }

  public EnumSelection<StartTask> StartUp { get; private set; }
  public EnumSelection<DayOfWeek> Days { get; private set; }
  public EnumSelection<AdditionalOptions> Options { get; private set; }
}

И вот как легко связать флажки и переключатели с этим классом:

<StackPanel Orientation="Vertical">
  <StackPanel Orientation="Horizontal">
    <!-- Using RadioButtons for exactly 1 selection behavior -->
    <RadioButton IsChecked="{Binding StartUp[Manual]}">Manual</RadioButton>
    <RadioButton IsChecked="{Binding StartUp[Automatic]}">Automatic</RadioButton>
  </StackPanel>
  <StackPanel Orientation="Horizontal">
    <!-- Using CheckBoxes for 0 or Many selection behavior -->
    <CheckBox IsChecked="{Binding Days[Sunday]}">Sunday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Monday]}">Monday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Tuesday]}">Tuesday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Wednesday]}">Wednesday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Thursday]}">Thursday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Friday]}">Friday</CheckBox>
    <CheckBox IsChecked="{Binding Days[Saturday]}">Saturday</CheckBox>
  </StackPanel>
  <StackPanel Orientation="Horizontal">
    <!-- Using CheckBoxes for 0 or 1 selection behavior -->
    <CheckBox IsChecked="{Binding Options[OptionA]}">Option A</CheckBox>
    <CheckBox IsChecked="{Binding Options[OptionB]}">Option B</CheckBox>
  </StackPanel>
</StackPanel>
  1. Когда пользовательский интерфейс загружается, будет выбран переключатель «Ручной», и вы можете изменить свой выбор между «Ручной» или «Автоматический», но всегда должен быть выбран любой из них.
  2. Каждый день недели будет не отмечен, но любое количество из них может быть проверено или не отмечено.
  3. «Вариант A» и «Вариант B» будут изначально не отмечены. Вы можете проверить одно или другое, отметив, что один снимает флажок с другого (аналогично RadioButton), но теперь вы также можете снять оба флажка (чего нельзя сделать с RadioButton в WPF, именно поэтому CheckBox используется здесь)
Ник
источник
Предположим, у вас есть 3 элемента в списке StartTask, таких как {Undefined, Manual, Automatic}. По умолчанию вы хотите установить значение Undefined, потому что до тех пор, пока пользователь не установит значение, оно не определено. Кроме того: Как поступает SelectedItem? Ваша ViewModel не имеет SelectedStartTask.
user1040323
В моей ViewModel свойство StartUp является EnumSelection<StartTask>объектом. Если вы посмотрите на определение, EnumSelection<T>вы увидите, что оно имеет свойство Value. Таким образом, модель представления не должна иметь «SelectedStartTask». Вы бы использовали StartUp.Value. А что касается значения по умолчанию Undefined, см. 3-е перечисление AdditionalOptions, у него есть None вместо Undefined, но вы можете изменить его имя на любое другое.
Ник
1

Это работает для Checkbox тоже.

public class EnumToBoolConverter:IValueConverter
{
    private int val;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int intParam = (int)parameter;
        val = (int)value;

        return ((intParam & val) != 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        val ^= (int)parameter;
        return Enum.Parse(targetType, val.ToString());
    }
}

Привязка одного перечисления к нескольким флажкам.

Али Баят
источник
1
Я говорю вам большое «СПАСИБО» за услугу, которую вы сделали для меня. Это сработало как шарм для меня.
Эльхам Азадфар
0

Основано на EnumToBooleanConverter от Скотта. Я заметил, что метод ConvertBack не работает в Enum с кодом flags.

Я пробовал следующий код:

public class EnumHasFlagToBooleanConverter : IValueConverter
    {
        private object _obj;
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            _obj = value;
            return ((Enum)value).HasFlag((Enum)parameter);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value.Equals(true))
            {
                if (((Enum)_obj).HasFlag((Enum)parameter))
                {
                    // Do nothing
                    return Binding.DoNothing;
                }
                else
                {
                    int i = (int)_obj;
                    int ii = (int)parameter;
                    int newInt = i+ii;
                    return (NavigationProjectDates)newInt;
                }
            }
            else
            {
                if (((Enum)_obj).HasFlag((Enum)parameter))
                {
                    int i = (int)_obj;
                    int ii = (int)parameter;
                    int newInt = i-ii;
                    return (NavigationProjectDates)newInt;

                }
                else
                {
                    // do nothing
                    return Binding.DoNothing;
                }
            }
        }
    }

Единственное, что я не могу получить на работе - это выполнить приведение intк тому, targetTypeчтобы я сделал это жестко NavigationProjectDates, к перечислению, которое я использую. И targetType == NavigationProjectDates...


Редактировать для более общего конвертера Flags Enum:

    открытый класс FlagsEnumToBooleanConverter: IValueConverter {
        private int _flags = 0;
        открытый объект Convert (значение объекта, тип targetType, параметр объекта, строковый язык) {
            if (value == null) return false;
            _flags = (int) значение;
            Тип t = значение. GetType ();
            объект o = Enum.ToObject (t, параметр);
            return ((Enum) значение) .HasFlag ((Enum) o);
        }

        открытый объект ConvertBack (значение объекта, тип targetType, параметр объекта, строковый язык)
        {
            if (значение? .Equals (true) ?? false) {
                _flags = _flags | (int) параметр;
            }
            еще {
                _flags = _flags & ~ (int) параметр;
            }
            вернуть _flags;
        }
    }
KenGey
источник
Кто-то отредактировал мой ответ, добавив его в код с флагами, так что, честно говоря, я никогда не пробовал и не использовал его сам, и подумал об его удалении, так как думаю, что это более логично, чем его собственный ответ. Если я смогу найти какое-то время спустя, я могу попытаться собрать что-то вместе, чтобы протестировать этот код, а также то, что у вас есть, и, возможно, помочь найти лучшее решение вашей проблемы.
Скотт
0

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

Конкретные шаги приведены ниже: создайте ListBox и задайте ItemsSource для списка как enum MyLovelyEnumи привяжите SelectedItem ListBox к VeryLovelyEnumсвойству. Затем будут созданы радиокнопки для каждого элемента ListBoxItem.

  • Шаг 1 : добавьте enum к статическим ресурсам для вашего Window, UserControl или Grid и т. Д.
    <Window.Resources>
        <ObjectDataProvider MethodName="GetValues"
                            ObjectType="{x:Type system:Enum}"
                            x:Key="MyLovelyEnum">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:MyLovelyEnum" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>
  • Шаг 2 : Используйте список и Control Templateзаполните каждый элемент внутри как кнопку-переключатель
    <ListBox ItemsSource="{Binding Source={StaticResource MyLovelyEnum}}" SelectedItem="{Binding VeryLovelyEnum, Mode=TwoWay}" >
        <ListBox.Resources>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <RadioButton
                                Content="{TemplateBinding ContentPresenter.Content}"
                                IsChecked="{Binding Path=IsSelected,
                                RelativeSource={RelativeSource TemplatedParent},
                                Mode=TwoWay}" />
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.Resources>
    </ListBox>

Преимущество ниже: если когда - нибудь ваши изменения класса перечислений, вам не нужно обновлять графический интерфейс (XAML - файл).

Ссылки: https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/

Браво Йенг
источник