Como vincular RadioButtons a um enum?

406

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.

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

Agora, como vinculo os RadioButtons à propriedade para uma ligação bidirecional adequada?

Sam
fonte
3
Se você deseja fazer isso sem especificar RadioButtons individuais no seu XAML, eu recomendaria um ListBox vinculado aos valores de enumeração como este ou este , e que tenha o modelo do item substituído para usar RadioButtons como este .
Rachel

Respostas:

389

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:

<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
fonte
51
Caiu como uma luva para mim. Além disso, modifiquei o ConvertBack para retornar também UnsetValue em "false", porque o silverlight (e provavelmente o WPF adequado) chama o conversor duas vezes - uma vez ao desabilitar o valor do botão de opção antigo e novamente para definir o novo. Eu estava pendurando outras coisas no estabelecimento, então só queria que ele chamasse uma vez. - if (parameterString == null || value.Equals (false)) retorna DependencyProperty.UnsetValue;
21810 MarcE
8
Pelo que sei, isso deve ser feito, a menos que os botões de opção estejam em grupos diferentes (e os botões AFAIK sem o GroupName definido com o mesmo pai estão por padrão no mesmo grupo). Caso contrário, as chamadas para definir a propriedade "devolver" e resultar em comportamento estranho.
Nlawalker 27/05
2
sim, mas se você chamar Unset no conversor ao definir false, não será um verdadeiro EnumToBooleanConverter, mas mais um EnumToRadioButtonConverter. Então, em vez disso, verifico se o valor é diferente no meu configurador de propriedades: if (_myEnumBackingField == value) return;
Stéphane
8
A ligação nesta solução só funciona corretamente unidirecional. Não foi possível alternar programaticamente o botão de opção atribuindo a propriedade binded a um valor diferente. Se você quer uma solução adequada e melhor, use a abordagem de Scott.
L46kok # 16/12
2
@ Marc, não é a coisa certa a retornar nesse caso 'Binding.DoNothing' e não 'DependencyProperty.UnsetValue'?
Mark A. Donohoe
560

Você pode simplificar ainda mais a resposta aceita. Em vez de digitar as enumerações como seqüências de caracteres no xaml e fazer mais trabalho no conversor do que o necessário, você pode transmitir explicitamente o valor da enumeração, em vez de uma representação de sequência, e, como comentou o CrimsonX, os erros são gerados no momento da compilação, em vez do tempo de execução:

ConverterParameter = {x: local estático: 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>

Em seguida, simplifique o conversor:

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

Editar (16 de dez '10):

Agradecemos a anon por sugerir o retorno de Binding.DoNothing em vez de DependencyProperty.UnsetValue.


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

No xaml, se os botões de opção compartilharem o mesmo contêiner pai, a seleção de um desmarcará todos os outros contidos nesse contêiner (mesmo que estejam vinculados a uma propriedade diferente). Portanto, tente manter o seu RadioButton vinculado 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 possam compartilhar um único contêiner pai, defina a propriedade GroupName de cada RadioButton como um valor comum para agrupá-los logicamente.


Editar (5 de abril de 11):

Caso contrário do ConvertBack simplificado para usar um Operador Ternário.


Nota - Tipo de enum aninhado em uma classe (28 de abril de 11):

Se o seu tipo de enumeração estiver aninhado em uma classe (em vez de diretamente no espaço de nomes), você poderá usar a sintaxe '+' para acessar a enumeração no XAML, conforme indicado em uma resposta (não marcada) à pergunta Não é possível encontrar tipo de enum para referência estática no WPF :

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

Devido a este problema do Microsoft Connect , no entanto, o designer no VS2010 não carregará mais a declaração "Type 'local:YourClass+YourNestedEnumType' was not found.", mas o projeto é compilado e executado com êxito. Obviamente, você pode evitar esse problema se conseguir mover seu tipo de enum diretamente para o espaço para nome.


Editar (27 de janeiro de 12):

Se estiver usando sinalizadores Enum, o conversor seria 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 é perguntado na pergunta, mas pode ser necessário em alguns casos, por exemplo, ORM retornando nulo do DB ou sempre que possa fazer sentido que na lógica do programa o valor não seja fornecido), lembre-se de adicionar uma verificação nula inicial no método Convert e retorne o valor bool apropriado, que normalmente é falso (se você não deseja selecionar nenhum botão de opção), 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);
    }

Nota - NullReferenceException (10 de outubro de 18):

Atualizado o exemplo para remover a possibilidade de lançar uma NullReferenceException. IsCheckedé um tipo anulável, portanto, retornar Nullable<Boolean>parece uma solução razoável.

Scott
fonte
26
Eu concordo, acredito que esta é uma solução melhor. Além disso, o uso dessa conversão fará com que o projeto seja interrompido no tempo de compilação, não no tempo de execução, se os valores da enumeração forem alterados, o que é uma grande vantagem.
CrimsonX
4
Esta é certamente uma solução muito melhor do que a aceita. 1
OrPaz
7
Ótima solução. Eu acrescentaria que este é realmente apenas um conversor de comparação comparando 2 valores. Ele poderia ter um nome mais genérico do que EnumToBooleanConverter como ComparisonConverter
MikeKulls
5
@ Scott, muito bom. Este conversor é bom em qualquer caso, com ou sem o atributo Flags. Mas seria bobagem, na maioria dos casos, usar esse filtro diretamente como conversor com enumeração como sinalizadores. O motivo é que você deve obter um cálculo booleano (| = ou ^ =) com o valor anterior para obter o resultado adequado, mas o conversor não tem acesso ao valor anterior. Você deve adicionar um booleano para cada valor de enumeração e fazer o cálculo booleano adequado no seu modelo MVVM. Mas obrigado por todas as informações, muito úteis.
precisa
2
No Windows Phone 8 (possivelmente no caso de aplicativos da Win Store), não temos x: static, portanto não podemos usar diretamente a solução aqui. No entanto, o IDE / Complier é inteligente o suficiente e procura a string em todos os literais (de qualquer maneira) por exemplo, isso funciona <RadioButton IsChecked = "{Binding TrackingMode, ConverterParameter = Driving, Converter = {StaticResource EnumToBooleanConverter}, Mode = TwoWay}" /> Quaisquer erros de digitação no Driving serão capturados durante o tempo de design / compilação, em vez do tempo de execução.
Adarsha
26

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 se deseja nesse caso.

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

anon
fonte
Não há ligação.Nada no Silverlight. É apenas WPF. Use nulo.
Alexander Vasilyev
1
Binding.Nothing também desapareceu da UWP.
BlackICE
5

Eu usaria os RadioButtons em um ListBox e vincularia ao SelectedValue.

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

Martin Moser
fonte
Eu recebo uma ligação bidirecional usando um método semelhante usando um ListBox e DataTemplate, então você deve.
Bryan Anderson
Este bug: geekswithblogs.net/claraoscura/archive/2008/10/17/125901.aspx arruinou um dia para mim.
Slampen
3
Esta é de longe a melhor solução, tudo o resto causa código redundante. ( Outro exemplo de uso de um ListBox)
HB
3

Para o 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.

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

Exemplo 2

Válido para WPF e UWP.

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

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

Exemplo 3

Válido apenas para o WPF!

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

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

Solução

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

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

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

O motivo pelo qual ele deve ser encaixotado é que aparentemente não há como referenciar o tipo no ConvertBackmétodo; o boxe cuida disso. Se você seguir um dos dois primeiros exemplos, poderá referenciar o tipo de parâmetro, eliminando a necessidade de herdar de uma classe in a box; se você deseja fazer tudo em uma linha e com o mínimo de detalhamento possível, a última solução é ideal.

O uso se assemelha ao Exemplo 2 , mas é, de fato, menos detalhado.

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

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

James M
fonte
1

Eu criei uma nova classe para manipular RadioButtons e CheckBoxes a enumerações. Funciona para enumerações sinalizadas (com várias seleções de caixas de seleção) e enumerações não sinalizadas para caixas 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 começo, no entanto, depois que você copia essa classe em seu projeto, está pronto. É genérico e pode ser reutilizado facilmente para qualquer enumeração.

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

E para saber como usá-lo, digamos que você tenha uma enumeração para executar uma tarefa manual 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, veja como é fácil usar esta classe:

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

E aqui está como é fácil vincular caixas de seleção e botões de opção a esta classe:

<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. Quando a interface do usuário é carregada, o botão de opção "Manual" será selecionado e você poderá alterar sua seleção entre "Manual" ou "Automático", mas cada um deles sempre deve ser selecionado.
  2. Todos os dias da semana serão desmarcados, mas qualquer número deles pode ser marcado ou desmarcado.
  3. "Opção A" e "Opção B" serão desmarcadas inicialmente. Você pode marcar um ou outro, marcar um desmarcará o outro (semelhante ao RadioButtons), mas agora também pode desmarcar os dois (o que não é possível com o RadioButton do WPF, e é por isso que o CheckBox está sendo usado aqui)
usuario
fonte
Suponha que você tenha 3 itens na enumeração StartTask, como {Indefinido, Manual, Automático}. Você deseja padronizar para Indefinido porque, até que um usuário defina um valor, ele não é definido. Além disso: Como é tratado o Item Selecionado? Seu ViewModel não possui SelectedStartTask.
user1040323
No meu ViewModel, a propriedade StartUp é um EnumSelection<StartTask>objeto. Se você olhar para a definição, EnumSelection<T>poderá ver que ela possui uma propriedade Value. Portanto, o modelo de visualização não precisa ter uma "SelectedStartTask". Você usaria StartUp.Value. E, para ter um valor padrão de Indefinido, consulte o 3º enum, AdditionalOptions, ele possui None em vez de Indefinido, mas você pode alterar o nome para o que quiser.
19419 Nick
1

Este trabalho para a caixa de seleção 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());
    }
}

Vincular uma única enumeração a várias caixas de seleção.

Ali Bayat
fonte
1
Eu digo um grande "OBRIGADO" pelo favor que você fez por mim. Funcionou como um encanto para mim.
Elham Azadfar
0

Baseado no EnumToBooleanConverter de Scott. Notei que o método ConvertBack não funciona no código Enum com 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 elenco de intpara, targetTypeentão eu o codifiquei para NavigationProjectDateso enum que eu uso. E targetType == NavigationProjectDates...


Edite para um conversor genérico de Flags Enum:

    classe pública FlagsEnumToBooleanConverter: IValueConverter {
        private int _flags = 0;
        objeto público Convert (valor do objeto, Type targetType, parâmetro do objeto, idioma da string) {
            if (value == null) retorna false;
            _flags = valor (int);
            Digite t = value.GetType ();
            objeto o = Enum.ToObject (t, parâmetro);
            return ((Enum) value) .HasFlag ((Enum) o);
        }

        objeto público ConvertBack (valor do objeto, Tipo targetType, parâmetro do objeto, idioma da string)
        {
            if (value? .Equals (true) ?? false) {
                _flags = _flags | parâmetro (int);
            }
            outro {
                _flags = _flags & ~ (int) parâmetro;
            }
            return _flags;
        }
    }
KenGey
fonte
Alguém havia editado minha resposta para adicionar o código Flags, honestamente, nunca tentei / usei sozinho e pensei em removê-lo, pois acho que faz mais sentido como sua própria resposta. Se eu encontrar algum tempo depois, posso tentar criar algo para testar esse código, o que você tem e talvez ajudar a encontrar uma solução melhor para o seu problema.
Scott
0

Você pode criar os botões de opção dinamicamente, ListBoxpode ajudá-lo a fazer isso, sem conversores, bastante simples.

As etapas do concreate estão abaixo: crie um ListBox e defina o ItemsSource para o listbox como a enumeração MyLovelyEnume vinculando o SelectedItem do ListBox à VeryLovelyEnumpropriedade Em seguida, os botões de opção para cada ListBoxItem serão criados.

  • Etapa 1 : adicione a enumeração aos recursos estáticos para sua Janela, UserControl ou Grade etc.
    <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>
  • Etapa 2 : use a caixa de listagem e Control Templatepara preencher cada item dentro como botão de opção
    <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>

A vantagem está abaixo: se algum dia sua classe enum mudar, você não precisará atualizar a GUI (arquivo XAML).

Referências: https://brianlagunas.com/a-better-way-to-data-bind-enums-in-wpf/

Bravo Yeung
fonte