У меня есть такое перечисление:
public enum MyLovelyEnum
{
FirstSelection,
TheOtherSelection,
YetAnotherOne
};
У меня есть свойство в моем DataContext:
public MyLovelyEnum VeryLovelyEnum { get; set; }
И в моем клиенте WPF есть три RadioButton.
<RadioButton Margin = "3">First Selection</RadioButton>
<RadioButton Margin = "3">The Other Selection</RadioButton>
<RadioButton Margin = "3">Yet Another one</RadioButton>
Как теперь привязать RadioButtons к свойству для правильной двусторонней привязки?





Я бы использовал 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.asp x испортил мне день.
Это, безусловно, лучшее решение, все остальное вызывает избыточный код. (Другой пример использования ListBox)
Вы можете использовать более общий конвертер
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>
Сработало для меня как шарм. В качестве дополнения я модифицировал ConvertBack, чтобы он также возвращал UnsetValue на «false», потому что silverlight (и, предположительно, собственно WPF) вызывает конвертер дважды - один раз при отключении старого значения переключателя и еще раз для установки нового. Я вешал другие вещи на установщик свойств, поэтому я хотел, чтобы он вызывал только один раз. - if (parameterString == null || value.Equals (false)) return DependencyProperty.UnsetValue;
Насколько я могу судить, этот должен должен выполняться, если только радиокнопки не находятся в разных группах (и кнопки AFAIK без набора GroupName, у которых есть один и тот же родительский элемент, по умолчанию находятся в одной и той же группе). В противном случае вызовы установки свойства «bounce» приведут к странному поведению.
да, но если вы вызываете Unset в конвертере при установке значения false, то это не настоящий EnumToBooleanConverter, а скорее EnumToRadioButtonConverter. Поэтому вместо этого я проверяю, отличается ли значение в моем установщике свойств: if (_myEnumBackingField == value) return;
Привязка этого решения работает правильно только в одном направлении. Я не мог программно переключить радиокнопку, присвоив привязанному свойству другое значение. Если вам нужно правильно работающее И лучшее решение, используйте подход Скотта.
@Marc, разве в этом случае не следует возвращать Binding.DoNothing, а не DependencyProperty.UnsetValue?
@MarquelV - Возможно, но в то время, когда я написал этот комментарий, я работал с Silverlight, у которого не было этой опции. Только что проверил документацию и похоже, что ее все еще нет :-(
Привилегия «видеть подсчет голосов», которую вы получаете при 1000 репутации, является убедительным доказательством того, что «ненавистники будут ненавидеть».
Повторение вышеупомянутого примечания: ЭТО НЕ РАБОТАЕТ ДВОЙНЫМ ОБРАЗОМ. Вы не можете установить свойство и правильно изменить IsChecked радиокнопки. Вы действительно хотите использовать метод Скотта, представленный ниже.
Я второй @ClintStLaurent. Это неправильный ответ, потому что он неправильно выполняет двустороннюю привязку. Ответ Скотта имеет значение.
Насколько я могу судить, делает работает правильно в двустороннем режиме, если вы примените исправление MarcE.
Что такое я в <l: EnumBooleanConverter x: Key = "enumBooleanConverter" /> @Lars. Вы можете объяснить, я не знаю, что это такое.
Чтобы использовать конвертер, вы должны добавить его пространство имен с тегом xmlns в свой xaml. См., Например, stackoverflow.com/questions/5399997/xaml-what-is-local
Вы можете еще больше упростить принятый ответ. Вместо того, чтобы вводить перечисления в виде строк в xaml и выполнять больше работы в вашем конвертере, чем необходимо, вы можете явно передать значение перечисления вместо строкового представления, и, как прокомментировал CrimsonX, ошибки возникают во время компиляции, а не во время выполнения:
<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;
}
}
ConverterParameter = {x: Статический локальный: YourClass +YourNestedEnumType.Enum1}
Однако из-за этого Проблема с Microsoft Connect конструктор в VS2010 больше не будет загружать указание "Type 'local:YourClass+YourNestedEnumType' was not found.", но проект компилируется и запускается успешно. Конечно, вы можете избежать этой проблемы, если сможете напрямую переместить свой тип перечисления в пространство имен.
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;
}
}
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);
}
IsChecked is a nullable type so returning Nullable<Boolean> seems a reasonable solution.
Я согласен, я считаю, что это лучшее решение. Кроме того, использование этого преобразования приведет к прерыванию проекта во время компиляции, а не во время выполнения, если значения перечисления изменяются, что является большим преимуществом.
Это, безусловно, гораздо лучшее решение, чем принятое. +1
Хорошее решение. Я бы добавил, что это действительно просто конвертер для сравнения двух значений. Он может иметь более общее имя, чем EnumToBooleanConverter, например ComparisonConverter.
@MikeKulls - спасибо! ComparisonConverter теперь часть моего арсенала конвертера :)
@ Скотт, очень мило. Этот конвертер хорош в любых случаях, с атрибутом Flags или без него. Но в большинстве случаев было бы глупо использовать этот фильтр непосредственно как преобразователь с перечислением в качестве флагов. Причина в том, что вы должны получить логическое вычисление (| = или ^ =) с предыдущим значением, чтобы получить правильный результат, но конвертер не имеет доступа к предыдущему значению. Затем вы должны добавить логическое значение для каждого значения перечисления и самостоятельно выполнить правильное логическое вычисление в своей модели MVVM. Но спасибо за каждую информацию, очень полезную.
@ Эрик Уэлле, спасибо. Кто-то еще обновил мой ответ кодом для Enum Flags. Я сам никогда не использовал это и думал о том, чтобы откатить ответ, так как это может еще больше запутать людей и загромождать ответ.
Собственно, это правильное решение, а не указанное выше. Привязка принятого ответа работает только в одну сторону, но это работает в обе стороны.
Метод Enum with flags ConvertBack не работает. В своем ответе я пробовал другой метод.
Вау, это хорошо, к сожалению, в Windows phone нет x: Static, поэтому мне пришлось придерживаться решения Lars.
В Windows Phone 8 (возможно, в случае Win Store Apps) у нас нет x: static, поэтому мы не можем напрямую использовать решение здесь. Однако IDE / Complier достаточно умен и ищет строку по всем строковым литералам (по крайней мере, я предполагаю). например это работает <RadioButton IsChecked = "{Binding TrackingMode, ConverterParameter = Driving, Converter = {StaticResource EnumToBooleanConverter}, Mode = TwoWay}" /> Любые опечатки в Driving будут обнаружены во время разработки / компиляции, а не во время выполнения.
В Silverlight нет Binding.DoNothing. Это только WPF. Вместо этого используйте null.
И в Silverlight также нет x: Static.
@Scott Верно, он помечен как WPF, и я не проголосовал против, а вместо этого проголосовал за. В то же время, я должен сказать, что я погуглил 'silverlight bind radiobutton enum' и нашел эту страницу :) Google должен настроить свою поисковую систему, чтобы учитывать теги stackoverflow :) Так что лучше оставлять свои комментарии для тех, кто заходит сюда как я.
Отличный пост! Потратил полдня, пробуя другие (более сложные) решения, но это просто и элегантно. Еще раз спасибо.
Это решение отлично работает, но исходная радиокнопка не выбирается, когда перечисление уже установлено. Например, у меня есть пациент с перечислением Gender (мужчина и женщина). Когда я запускаю приложение, флажки не устанавливаются. Но когда я проверяю один из них, свойство Gender обновляется правильно.
@RvdK неважно, это не сработало, потому что я установил имя группы в обоих радиокнопках. Когда удалил, работает.
@Scott value (найденный в методах Convert() и ConvertBack()) может иметь значение null. Предлагаю написать соответственно return (value?.Equals(parameter) ?? false); и return (((bool?)value ?? false) ? parameter : Binding.DoNothing);. По-прежнему однострочные, по-прежнему чистые и простые для чтения. :)
Никто не заметил в журнале отладки что-то вроде: [0:] Binding: 'System.Object' can not be converted to type local:YourClass+YourNestedEnumType при снятии галочки?
Для ответа EnumToBooleanConverter: Вместо возврата DependencyProperty.UnsetValue рассмотрите возможность возврата Binding.DoNothing в случае, когда значение IsChecked переключателя становится ложным. Первый указывает на проблему (и может показать пользователю красный прямоугольник или аналогичные индикаторы проверки), а второй просто указывает, что ничего делать не следует, что и требуется в этом случае.
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.convertback.aspxhttp://msdn.microsoft.com/en-us/library/system.windows.data.binding.donothing.aspx
В Silverlight нет привязки. Это только WPF. Вместо этого используйте null.
Связывание. Ничего не пропало из UWP.
На основе EnumToBooleanConverter от Скотта. Я заметил, что метод ConvertBack не работает с кодом Enum с флагами.
Я пробовал следующий код:
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:
public class FlagsEnumToBooleanConverter : IValueConverter {
private int _flags=0;
public object Convert(object value, Type targetType, object parameter, string language) {
if (value == null) return false;
_flags = (int) value;
Type t = value.GetType();
object o = Enum.ToObject(t, parameter);
return ((Enum)value).HasFlag((Enum)o);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value?.Equals(true) ?? false) {
_flags = _flags | (int) parameter;
}
else {
_flags = _flags & ~(int) parameter;
}
return _flags;
}
}
Кто-то отредактировал мой ответ, чтобы добавить в код с флагами, поэтому, честно говоря, я никогда не пробовал / не использовал его сам и думал об удалении, поскольку я думаю, что это имеет больше смысла как собственный ответ. Если я смогу найти время позже, я могу попытаться собрать что-нибудь, чтобы протестировать этот код, а также то, что у вас есть, и, возможно, помочь найти лучшее решение вашей проблемы.
Для 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);
}
}
Затем для каждого типа, который вы хотите поддерживать, определите преобразователь, который помещает перечисляемый тип в коробку.
public class MyEnumToBooleanConverter : EnumToBooleanConverter<MyEnum>
{
//Nothing to do!
}
Причина, по которой он должен быть помещен в коробку, заключается в том, что, по-видимому, нет возможности ссылаться на тип в методе ConvertBack; бокс позаботится об этом. Если вы воспользуетесь одним из первых двух примеров, вы можете просто сослаться на тип параметра, устраняя необходимость наследования от упакованного класса; если вы хотите сделать все в одну строку и с наименьшей возможной многословностью, последнее решение является идеальным.
Использование похоже на Пример 2, но на самом деле менее подробное.
<MyControl MyProperty = "{Binding AnotherProperty, Converter = {StaticResource MyEnumToBooleanConverter}, ConverterParameter=Field}"/>
Обратной стороной является необходимость определения конвертера для каждого типа, который вы хотите поддерживать.
Это работает и для Флажок.
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());
}
}
Привязка одного перечисления к нескольким флажкам.
Я говорю вам большое "СПАСИБО" за оказанную мне услугу. Для меня это сработало как оберег.
Я создал новый класс для обработки привязки RadioButtons и CheckBox к перечислениям. Он работает для помеченных перечислений (с множественным выбором флажков) и неотмеченных перечислений для флажков с одним выбором или переключателей. Он также вообще не требует никаких преобразователей 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>
Предположим, у вас есть 3 элемента в перечислении StartTask, например {Undefined, Manual, Automatic}. Вы хотите установить по умолчанию Undefined, потому что, пока пользователь не установит значение, оно не определено. Также: как поступают с SelectedItem? В вашей ViewModel нет SelectedStartTask.
В моей модели ViewModel свойство StartUp - это объект EnumSelection<StartTask>. Если вы посмотрите определение EnumSelection<T>, вы увидите, что у него есть свойство Value. Таким образом, модель представления не обязательно должна иметь «SelectedStartTask». Вы бы использовали StartUp.Value. А что касается значения по умолчанию Undefined, см. 3-е перечисление AdditionalOptions, у него есть None вместо Undefined, но вы можете изменить его имя на любое другое.
Вы можете создавать переключатели динамически, ListBox может помочь вам сделать это без конвертеров, довольно просто.
Конкретные шаги приведены ниже:
MyLovelyEnum и привяжите SelectedItem ListBox к свойству VeryLovelyEnum. <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>
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/
Это не сработает, если EnumProperty уже имеет значение. public MyEnum EProperty {get; set;} = MyEnum.Value1. RadioButton не проверяется
Решение TwoWay Binding для UWP, которое использует Nullable:
Часть C#:
public class EnumConverter : IValueConverter
{
public Type EnumType { get; set; }
public object Convert(object value, Type targetType, object parameter, string lang)
{
if (parameter is string enumString)
{
if (!Enum.IsDefined(EnumType, value)) throw new ArgumentException("value must be an Enum!");
var enumValue = Enum.Parse(EnumType, enumString);
return enumValue.Equals(value);
}
return value.Equals(Enum.ToObject(EnumType,parameter));
}
public object ConvertBack(object value, Type targetType, object parameter, string lang)
{
if (parameter is string enumString)
return value?.Equals(true) == true ? Enum.Parse(EnumType, enumString) : null;
return value?.Equals(true) == true ? Enum.ToObject(EnumType, parameter) : null;
}
}
Здесь значение null действует как Binding.DoNothing.
private YourEnum? _yourEnum = YourEnum.YourDefaultValue; //put a default value here
public YourEnum? YourProperty
{
get => _yourEnum;
set{
if (value == null) return;
_yourEnum = value;
}
}
Часть Xaml:
...
<Page.Resources>
<ResourceDictionary>
<helper:EnumConverter x:Key = "YourConverter" EnumType = "yournamespace:YourEnum" />
</ResourceDictionary>
</Page.Resources>
...
<RadioButton GroupName = "YourGroupName" IsChecked = "{Binding Converter = {StaticResource YourConverter}, Mode=TwoWay, Path=YourProperty, ConverterParameter=YourEnumString}">
First way (parameter of type string)
</RadioButton>
<RadioButton GroupName = "LineWidth">
<RadioButton.IsChecked>
<Binding
Converter = "{StaticResource PenWidthConverter}"
Mode = "TwoWay" Path = "PenWidth">
<Binding.ConverterParameter>
<yournamespace:YourEnum>YourEnumString</yournamespace:YourEnum>
</Binding.ConverterParameter>
</Binding>
</RadioButton.IsChecked>
Second way (parameter of type YourEnum (actually it was converted to int when passed to converter))
</RadioButton>
Если вы хотите сделать это без указания отдельных RadioButton в своем XAML, я бы порекомендовал ListBox, привязанный к значениям перечисления, таким как это или это, и в котором шаблон элемента перезаписан для использования RadioButtons, например это.