Como ligar RadioButtons a um enum?

Eu tenho um enum como este:

public enum MyLovelyEnum { FirstSelection, TheOtherSelection, YetAnotherOne }; 

Eu tenho uma propriedade no meu DataContext:

 public MyLovelyEnum VeryLovelyEnum { get; set; } 

E eu tenho três RadioButtons no meu cliente WPF.

 First Selection The Other Selection Yet Another one 

Agora, como vinculo os RadioButtons à propriedade para uma binding bidirecional adequada?

Você poderia usar um conversor mais genérico

 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 } 

E na parte XAML você usa:

      first selection the other selection yet another one   

Você pode simplificar ainda mais a resposta aceita. Em vez de digitar as enums como strings em xaml e fazer mais trabalho em seu conversor do que o necessário, você pode explicitamente passar o valor enum em vez de uma representação de string e, como CrimsonX comentou, erros são lançados em tempo de compilation em vez de runtime:

ConverterParameter = {x: local estático: YourEnumType.Enum1}

        

Então simplifique o conversor:

 public class EnumToBooleanConverter : 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) ? parameter : Binding.DoNothing; } } 

Nota – Vários grupos de RadioButtons no mesmo contêiner (17 de fevereiro de 2011):

Em xaml, se os botões de rádio compartilharem o mesmo contêiner pai, a seleção de um deles desmarcará todos os outros dentro desse contêiner (mesmo que estejam vinculados a uma propriedade diferente). Portanto, tente manter seus RadioButtons ligados a uma propriedade comum agrupada em seu próprio contêiner, como um painel de pilha. Nos casos em que seus RadioButtons relacionados não podem compartilhar um único container pai, defina a propriedade GroupName de cada RadioButton como um valor comum para agrupá-los logicamente.

Nota – Enum tipo nested em uma class (28 de abril ’11):

Se o seu tipo de enumeração estiver nested em uma class (em vez de diretamente no namespace), talvez seja possível usar a syntax ‘+’ para acessar o enum em XAML, conforme indicado em uma resposta (não marcada) à pergunta Não é possível encontrar tipo enum para referência estática no WPF :

ConverterParameter = {x: local estático: YourClass + YourNestedEnumType.Enum1}

Devido a esse problema do Microsoft Connect , no entanto, o designer no VS2010 não carregará mais informando "Type 'local:YourClass+YourNestedEnumType' was not found." , mas o projeto compila e executa com êxito. Naturalmente, você pode evitar esse problema se conseguir mover seu tipo de enum para o namespace diretamente.

Editar (16 de dezembro de 10):

Obrigado ao anon por sugerir o retorno de Binding.DoNothing em vez de DependencyProperty.UnsetValue.

Editar (5 de abril de 11):

ConvertBack Simplificado do if-else para usar um Operador Ternário.

Editar (27 de janeiro de 2012):

Se estiver usando sinalizadores Enum, o conversor será o seguinte:

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

Editar (7 de maio de 15):

No caso de um Enum Nullable (que não é solicitado na questão, mas pode ser necessário em alguns casos, por exemplo, ORM retornando null do DB ou sempre que possa fazer sentido que na lógica do programa o valor não é fornecido), lembre-se de adicionar uma verificação nula inicial no método de conversão e retorna o valor de bool apropriado, que é normalmente falso (se você não quiser nenhum botão de opção selecionado), como abaixo:

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

Para a resposta EnumToBooleanConverter: Em vez de retornar DependencyProperty.UnsetValue, considere retornar Binding.DoNothing para o caso em que o valor do botão de opção IsChecked se torna falso. O primeiro indica um problema (e pode mostrar ao usuário um retângulo vermelho ou indicadores de validação semelhantes), enquanto o último apenas indica que nada deve ser feito, que é o que é desejado nesse caso.

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

Eu usaria os RadioButtons em um ListBox e, em seguida, vincular ao SelectedValue.

Este é um tópico mais antigo sobre esse tópico, mas a ideia básica deve ser a mesma: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f-8d99-fecf45522395/

Para UWP, não é tão simples: você deve pular um aro extra para passar um valor de campo como parâmetro.

Exemplo 1

Válido para WPF e UWP.

     Field     

Exemplo 2

Válido para WPF e UWP.

 ... Field ...  

Exemplo 3

Válido apenas para o WPF!

  

UWP não suporta x:Static então o Exemplo 3 está fora de questão; supondo que você vá com o Exemplo 1 , o resultado é um código mais detalhado. O exemplo 2 é um pouco melhor, mas ainda não é ideal.

Solução

 public abstract class EnumToBooleanConverter : 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); } } 

Em seguida, para cada tipo que você deseja suportar, defina um conversor que contenha o tipo de enumeração.

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

A razão pela qual deve ser encaixotada é porque aparentemente não há como referenciar o tipo no método ConvertBack ; o boxe cuida disso. Se você optar por um dos dois primeiros exemplos, poderá apenas referenciar o tipo de parâmetro, eliminando a necessidade de herdar de uma class em checkbox; Se você deseja fazer tudo em uma linha e com o mínimo de verbosidade possível, a última solução é ideal.

O uso se assemelha ao Exemplo 2 , mas é, na verdade, menos detalhado.

  

A desvantagem é que você deve definir um conversor para cada tipo que você deseja suportar.

Este trabalho para Checkbox também.

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

Vinculando um único enum a várias checkboxs de seleção.

Estendeu as grandes idéias acima com a capacidade de vincular botões de rádio a qualquer tipo (enumeração, Boolean, string, integer, etc.) e forneceu código de exemplo de trabalho aqui:

http://www.codeproject.com/Tips/720497/Binding-Radio-Buttons-to-a-Single-Property

Eu criei uma nova class para lidar com RadioButtons de binding e CheckBoxes para enums. Ele funciona para enums sinalizadas (com várias seleções de checkboxs de seleção) e enums sem sinal para checkboxs de seleção de seleção única ou botões de opção. Também não requer nenhum ValueConverters.

Isso pode parecer mais complicado no início, no entanto, depois de copiar essa class em seu projeto, está feito. É genérico, por isso pode ser facilmente reutilizado para qualquer enum.

 public class EnumSelection : 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)); } } 

E para saber como usá-lo, digamos que você tenha um enum para executar uma tarefa manualmente ou automaticamente, e pode ser agendado para qualquer dia da semana e algumas opções opcionais …

 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 } 

Agora, aqui está como é fácil usar essa class:

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

E aqui está como é fácil ligar checkboxs de seleção e botões de opção com esta class:

    Manual Automatic    Sunday Monday Tuesday Wednesday Thursday Friday Saturday    Option A Option B   
  1. Quando a interface do usuário for carregada, o botão de opção "Manual" será selecionado e você poderá alterar a sua seleção entre "Manual" ou "Automático", mas um deles sempre deverá estar selecionado.
  2. Todos os dias da semana serão desmarcados, mas qualquer número deles pode ser verificado ou desmarcado.
  3. "Opção A" e "Opção B" serão inicialmente desmarcadas. Você pode marcar um ou outro, marcando um deles e desmarcando o outro (similar ao RadioButtons), mas agora você também pode desmarcar ambos (o que você não pode fazer com o RadioButton do WPF, é por isso que o CheckBox está sendo usado aqui)

Baseado no EnumToBooleanConverter de Scott. Eu observei que o método ConvertBack não funciona no Enum com código de sinalizadores.

Eu tentei o seguinte código:

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

A única coisa que não consigo trabalhar é fazer um targetType de int para targetType então targetType o targetType de targetType para NavigationProjectDates , o enum que eu uso. E, targetType == NavigationProjectDates


Editar para o conversor mais genérico Flags Enum:

     Classe pública FlagsEnumToBooleanConverter: IValueConverter {
         private int _flags = 0;
         object público Convert (valor do object, tipo targetType, parâmetro de object, linguagem de string) {
             if (valor == null) retorna falso;
             _flags = (int) valor;
             Digite t = value.GetType ();
             object o = Enum.ToObject (t, parameter);
             return ((Enum) valor) .HasFlag ((Enum) o);
         }

         object público ConvertBack (valor do object, tipo targetType, parâmetro de object, linguagem de string)
         {
             if (valor? .Equals (true) ?? false) {
                 _flags = _flags |  parâmetro (int);
             }
             outro {
                 _flags = _flags & ~ (int) parâmetro;
             }
             return _flags;
         }
     }
Intereting Posts