How to bind RadioButtons to an enum?
Asked Answered
E

11

476

I've got an enum like this:

public enum MyLovelyEnum
{
    FirstSelection,
    TheOtherSelection,
    YetAnotherOne
};

I got a property in my DataContext:

public MyLovelyEnum VeryLovelyEnum { get; set; }

And I got three RadioButtons in my WPF client.

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

Now how do I bind the RadioButtons to the property for a proper two-way binding?

Emissary answered 29/12, 2008 at 11:35 Comment(1)
If you're looking to do this without specifying individual RadioButtons in your XAML, I would recommend a ListBox bound to the enum values like this or this, and that has the item template overwritten to use RadioButtons like this.Marya
K
417

You could use a more generic converter

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
}

And in the XAML-Part you use:

<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>
Kuehnel answered 2/1, 2009 at 13:36 Comment(12)
Worked like a charm for me. As an addition I modified ConvertBack to also return UnsetValue on "false", because silverlight (and presumably WPF proper) calls the converter twice - once when unsetting the old radio button value and again to set the new one. I was hanging other things off the property setter so I only wanted it called once. -- if (parameterString == null || value.Equals(false)) return DependencyProperty.UnsetValue;Marylyn
From what I can tell, this must be done unless the radio buttons are in different groups (and AFAIK buttons without GroupName set that have the same parent are by default in the same group). Otherwise, the calls to set the property "bounce" and result in odd behavior.Gunyah
yes but if you call Unset in the converter when setting to false, then it is not a true EnumToBooleanConverter but more a EnumToRadioButtonConverter. So instead I check if the value is different in my property setter : if (_myEnumBackingField == value) return;Rouleau
The binding on this solution only works correctly one-way. I could not programmatically toggle the radiobutton by assigning the binded property to a different value. If you want a proper working AND better solution, use scott's approach.Fitz
@Marc, isn't the proper thing to return in that case 'Binding.DoNothing' and not 'DependencyProperty.UnsetValue'?Eclectic
@MarquelV - Probably, but at the time I wrote that comment I was working with Silverlight which didn't have that option. Just checked the documentation and it looks like it's still missing :-(Marylyn
Repeating the above note: THIS DOES NOT WORK TWO WAY. You cannot set the property and have the radiobutton's change their IsChecked correctly. You really want to use the method below by Scott.Summerville
I second @ClintStLaurent. This is not the correct answer because it does not correctly two-way bind. Scott's answer does.Cardew
As far as I can tell, it does work correctly two-way, if you apply MarcE's fixStan
What is I in <l:EnumBooleanConverter x:Key="enumBooleanConverter" /> @Kuehnel Can you explain, I dont know what this is.Vraisemblance
To use the converter you have to add its namespace with a xmlns tag to your xaml. See e.g. https://mcmap.net/q/81123/-xaml-what-is-quot-local-quotKuehnel
To make this solution work in both directions, the Binding.DoNothing of Scotts answer above needs to be added ... return ((bool)value) ? Enum.Parse(targetType, parameterString) : Binding.DoNothing; When the binded value is set to another EnumValue, one radio will be set and the one that was set before will be unset. This seems to call ConvertBack() with value = false and would return the old EnumValue which is wrong! To avoid this effect, ConvertBack() is allowed to return a EnumValue only in case value == true, otherwise it returns Binding.DoNothing.Walsingham
R
637

You can further simplify the accepted answer. Instead of typing out the enums as strings in xaml and doing more work in your converter than needed, you can explicitly pass in the enum value instead of a string representation, and as CrimsonX commented, errors get thrown at compile time rather than runtime:

ConverterParameter={x:Static local: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>

Then simplify the converter:

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

Edit (Dec 16 '10):

Thanks to anon for suggesting returning Binding.DoNothing rather than DependencyProperty.UnsetValue.


Note - Multiple groups of RadioButtons in same container (Feb 17 '11):

In xaml, if radio buttons share the same parent container, then selecting one will de-select all other's within that container (even if they are bound to a different property). So try to keep your RadioButton's that are bound to a common property grouped together in their own container like a stack panel. In cases where your related RadioButtons cannot share a single parent container, then set the GroupName property of each RadioButton to a common value to logically group them.

Edit (Apr 5 '11):

Simplified ConvertBack's if-else to use a Ternary Operator.

Note - Enum type nested in a class (Apr 28 '11):

If your enum type is nested in a class (rather than directly in the namespace), you might be able to use the '+' syntax to access the enum in XAML as stated in a (not marked) answer to the question :

ConverterParameter={x:Static local:YourClass+YourNestedEnumType.Enum1}

Due to this Microsoft Connect Issue, however, the designer in VS2010 will no longer load stating "Type 'local:YourClass+YourNestedEnumType' was not found.", but the project does compile and run successfully. Of course, you can avoid this issue if you are able to move your enum type to the namespace directly.


Edit (Jan 27 '12):

If using Enum flags, the converter would be as follows:
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;
    }
}

Edit (May 7 '15):

In case of a Nullable Enum (that is **not** asked in the question, but can be needed in some cases, e.g. ORM returning null from DB or whenever it might make sense that in the program logic the value is not provided), remember to add an initial null check in the Convert Method and return the appropriate bool value, that is typically false (if you don't want any radio button selected), like below:
    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);
    }

Note - NullReferenceException (Oct 10 '18):

Updated the example to remove the possibility of throwing a NullReferenceException. `IsChecked` is a nullable type so returning `Nullable` seems a reasonable solution.
Radiosensitive answered 25/5, 2010 at 22:7 Comment(17)
I agree, I believe this is a better solution. Also, using this conversion will cause the project to break at compile time, not run time, if the enumeration values are changed, which is a big advantage.Maggs
Nice solution. I would add that this is really just a comparison converter comparing 2 values. It could have a more generic name than EnumToBooleanConverter such as ComparisonConverterHamiltonian
@Hamiltonian - thanks! ComparisonConverter is now part of my converter arsenal :)Dredge
@ Scott, Very nice. This converter is good in any cases, with or without Flags attribute. But it would be silly in most case to use this filter directly as converter with enum as flags. The reason is that you should achieve boolean calc (|= or ^=) with previous value to get proper result but converter does not have access to previous value. You then should add a bool for each enum value and do the proper boolean calc yourself in your MVVM model. But thanks for every info, very usefull.Haynie
@Eric Ouellet, Thanks. Somebody else updated my answer with the code for Enum Flags. I've never used that myself either and was thinking of rolling back the answer as it might just confuse people more and clutters the answer.Radiosensitive
Actually, this is the correct solution, not the above one. The accepted answer's binding only works one way, but this works both ways.Fitz
The Enum with flags ConvertBack method doesn't work. I've tried another method in my answer.Coldblooded
Wow this is nice, unfortunately in Windows phone there's no x:Static so I had to stick to Lars solutionTintype
In Windows Phone 8 (Possibly in case of Win Store Apps) we don't have x:static, so we can't directly use the solution here. However the IDE/Complier is smart enough, and looks up the string against all string literals (my guess anyway). e.g. this works <RadioButton IsChecked="{Binding TrackingMode, ConverterParameter=Driving, Converter={StaticResource EnumToBooleanConverter}, Mode=TwoWay}" /> Any typos in Driving would be caught during design/compile time rather than run time.Mcbrayer
There is no Binding.DoNothing in Silverlight. It's WPF only. Use null instead.Laine
And there is also no x:Static in Silverlight.Laine
@Radiosensitive True, it's tagged as WPF, and I didn't downvoted, but upvoted instead. At the same time, I must say that I googled 'silverlight bind radiobutton enum' and found this page :) Google has to adjust its search engine to take stackoverflow tags into account :) So it's better to leave my comments for those who come here like me.Laine
Great post ! Spent half a day trying other (more complicated) solutions, but this is simple and elegant. Thanks again.Haeckel
This solution works great, but the initial radiobutton is not selected when the enum is already set. For example, I have a patient with a Gender enum (male and female). When I start up the application, no checkboxes are selected. But when I check one of them then the Gender property is properly updated.Lowtension
@Lowtension nevermind, it didn't work because I set the groupname in both the radiobuttons. When I removed it, it works.Lowtension
@Radiosensitive value (found within the Convert() and ConvertBack() methods) can be null. I suggest writing return (value?.Equals(parameter) ?? false); and return (((bool?)value ?? false) ? parameter : Binding.DoNothing); respectively. Still one-liners, still clean and simple to read. :)Gastroenterostomy
No one noticed in the debug log something like: [0:] Binding: 'System.Object' can not be converted to type local:YourClass+YourNestedEnumType upon unchecking ?Unimposing
K
417

You could use a more generic converter

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
}

And in the XAML-Part you use:

<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>
Kuehnel answered 2/1, 2009 at 13:36 Comment(12)
Worked like a charm for me. As an addition I modified ConvertBack to also return UnsetValue on "false", because silverlight (and presumably WPF proper) calls the converter twice - once when unsetting the old radio button value and again to set the new one. I was hanging other things off the property setter so I only wanted it called once. -- if (parameterString == null || value.Equals(false)) return DependencyProperty.UnsetValue;Marylyn
From what I can tell, this must be done unless the radio buttons are in different groups (and AFAIK buttons without GroupName set that have the same parent are by default in the same group). Otherwise, the calls to set the property "bounce" and result in odd behavior.Gunyah
yes but if you call Unset in the converter when setting to false, then it is not a true EnumToBooleanConverter but more a EnumToRadioButtonConverter. So instead I check if the value is different in my property setter : if (_myEnumBackingField == value) return;Rouleau
The binding on this solution only works correctly one-way. I could not programmatically toggle the radiobutton by assigning the binded property to a different value. If you want a proper working AND better solution, use scott's approach.Fitz
@Marc, isn't the proper thing to return in that case 'Binding.DoNothing' and not 'DependencyProperty.UnsetValue'?Eclectic
@MarquelV - Probably, but at the time I wrote that comment I was working with Silverlight which didn't have that option. Just checked the documentation and it looks like it's still missing :-(Marylyn
Repeating the above note: THIS DOES NOT WORK TWO WAY. You cannot set the property and have the radiobutton's change their IsChecked correctly. You really want to use the method below by Scott.Summerville
I second @ClintStLaurent. This is not the correct answer because it does not correctly two-way bind. Scott's answer does.Cardew
As far as I can tell, it does work correctly two-way, if you apply MarcE's fixStan
What is I in <l:EnumBooleanConverter x:Key="enumBooleanConverter" /> @Kuehnel Can you explain, I dont know what this is.Vraisemblance
To use the converter you have to add its namespace with a xmlns tag to your xaml. See e.g. https://mcmap.net/q/81123/-xaml-what-is-quot-local-quotKuehnel
To make this solution work in both directions, the Binding.DoNothing of Scotts answer above needs to be added ... return ((bool)value) ? Enum.Parse(targetType, parameterString) : Binding.DoNothing; When the binded value is set to another EnumValue, one radio will be set and the one that was set before will be unset. This seems to call ConvertBack() with value = false and would return the old EnumValue which is wrong! To avoid this effect, ConvertBack() is allowed to return a EnumValue only in case value == true, otherwise it returns Binding.DoNothing.Walsingham
G
28

For the EnumToBooleanConverter answer: Instead of returning DependencyProperty.UnsetValue consider returning Binding.DoNothing for the case where the radio button IsChecked value becomes false. The former indicates a problem (and might show the user a red rectangle or similar validation indicators) while the latter just indicates that nothing should be done, which is what is wanted in that case.

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

Gaudery answered 3/9, 2010 at 14:40 Comment(2)
There is no Binding.Nothing in Silverlight. It's WPF only. Use null instead.Laine
Binding.Nothing is also gone from UWP.Photocathode
L
5

I would use the RadioButtons in a ListBox, and then bind to the SelectedValue.

This is an older thread about this topic, but the base idea should be the same: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/323d067a-efef-4c9f-8d99-fecf45522395/

Lyndalynde answered 29/12, 2008 at 11:51 Comment(3)
I get a two-way binding doing a similar method using a ListBox and DataTemplate so you should.Lafferty
This bug: geekswithblogs.net/claraoscura/archive/2008/10/17/125901.aspx ruined a day for me.Nutria
This is by far the best solution, everything else causes redundant code. (Another example of using a ListBox)Tigress
U
3

For UWP, it is not so simple: You must jump through an extra hoop to pass a field value as a parameter.

Example 1

Valid for both WPF and UWP.

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

Example 2

Valid for both WPF and UWP.

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

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

Example 3

Valid only for WPF!

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

UWP doesn't support x:Static so Example 3 is out of the question; assuming you go with Example 1, the result is more verbose code. Example 2 is slightly better, but still not ideal.

Solution

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

Then, for each type you wish to support, define a converter that boxes the enum type.

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

The reason it must be boxed is because there's seemingly no way to reference the type in the ConvertBack method; the boxing takes care of that. If you go with either of the first two examples, you can just reference the parameter type, eliminating the need to inherit from a boxed class; if you wish to do it all in one line and with least verbosity possible, the latter solution is ideal.

Usage resembles Example 2, but is, in fact, less verbose.

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

The downside is you must define a converter for each type you wish to support.

University answered 18/3, 2017 at 6:4 Comment(0)
P
3

I've created a new class to handle binding RadioButtons and CheckBoxes to enums. It works for flagged enums (with multiple checkbox selections) and non-flagged enums for single-selection checkboxes or radio buttons. It also requires no ValueConverters at all.

This might look more complicated at first, however, once you copy this class into your project, it's done. It's generic so it can easily be reused for any enum.

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

And for how to use it, let's say you have an enum for running a task manually or automatically, and can be scheduled for any days of the week, and some optional options...

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
}

Now, here's how easy it is to use this class:

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

And here's how easy it is to bind checkboxes and radio buttons with this class:

<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. When the UI loads, the "Manual" radio button will be selected and you can alter your selection between "Manual" or "Automatic" but either one of them must always be selected.
  2. Every day of the week will be unchecked, but any number of them can be checked or unchecked.
  3. "Option A" and "Option B" will both initially be unchecked. You can check one or the other, checking one will uncheck the other (similar to RadioButtons), but now you can also uncheck both of them (which you cannot do with WPF's RadioButton, which is why CheckBox is being used here)
Prosthodontist answered 2/4, 2018 at 19:22 Comment(2)
Suppose you have 3 items in the StartTask enum such as { Undefined, Manual, Automatic } You wish to default to Undefined because until a user sets a value it is undefined. Also: How is the SelectedItem dealt with? Your ViewModel has no SelectedStartTask.Fusco
In my ViewModel, the StartUp property is a EnumSelection<StartTask> object. If you look at the definition of EnumSelection<T> you can see that it has a Value property. So the view model doesn't need to have a "SelectedStartTask". You would use StartUp.Value. And as for having a default value of Undefined, see the 3rd enum, AdditionalOptions, it has a None instead of an Undefined, but you can change the name of it to whatever you want.Prosthodontist
D
2

You can create the radio buttons dynamically, ListBox can help you do that, without converters, quite simple.

The concrete steps are below:

  • create a ListBox and set the ItemsSource for the listbox as the enum MyLovelyEnum and binding the SelectedItem of the ListBox to the VeryLovelyEnum property.
  • then the Radio Buttons for each ListBoxItem will be created.
  • Step 1: add the enum to static resources for your Window, UserControl or Grid 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>
  • Step 2: Use the List Box and Control Template to populate each item inside as Radio button
    <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>

The advantage is: if someday your enum class changes, you do not need to update the GUI (XAML file).

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

Duchamp answered 5/6, 2020 at 8:4 Comment(1)
This will not work, if the EnumProperty already has a value. public MyEnum EProperty {get; set;} = MyEnum.Value1. The RadioButton does not get checkedSoll
S
2

One way to handle this would be to have separate bool properties in your ViewModel class. Here is how I've handled with such a situation:

ViewModel:

public enum MyLovelyEnum { FirstSelection, TheOtherSelection, YetAnotherOne };
private MyLovelyEnum CurrentSelection;

public bool FirstSelectionProperty
{
    get
    {
        return CurrentSelection == MyLovelyEnum.FirstSelection;
    }
    set
    {
        if (value)
            CurrentSelection = MyLovelyEnum.FirstSelection;
    }
}

public bool TheOtherSelectionProperty
{
    get
    {
        return CurrentSelection == MyLovelyEnum.TheOtherSelection;
    }
    set
    {
        if (value)
            CurrentSelection = MyLovelyEnum.TheOtherSelection;
    }
}

public bool YetAnotherOneSelectionProperty
{
    get
    {
        return CurrentSelection == MyLovelyEnum.YetAnotherOne;
    }
    set
    {
        if (value)
            CurrentSelection = MyLovelyEnum.YetAnotherOne;
    }
}

XAML:

<RadioButton IsChecked="{Binding SimilaritySort, Mode=TwoWay}">Similarity</RadioButton>
<RadioButton IsChecked="{Binding DateInsertedSort, Mode=TwoWay}">Date Inserted</RadioButton>
<RadioButton IsChecked="{Binding DateOfQuestionSort, Mode=TwoWay}">Date of Question</RadioButton>
<RadioButton IsChecked="{Binding DateModifiedSort, Mode=TwoWay}">Date Modified</RadioButton>

It's not as robust or dynamic as some of the other solutions, but the nice thing is it's very self-contained and doesn't require creating custom converters or anything like that.

Schenck answered 17/9, 2021 at 21:47 Comment(1)
Yeah, unless this is a frequently recurring scenario, this is honestly something where I'd "take the L" and accept that WPF doesn't have innate, convenient support and do this instead. On the one off cases, I think it lends to improved maintainability.Lelandleler
P
1

This work for Checkbox too.

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

Binding a single enum to multiple checkboxes.

Plagal answered 25/12, 2017 at 18:41 Comment(0)
C
0

Based on the EnumToBooleanConverter from Scott. I noticed that the ConvertBack method doesn't work on the Enum with flags code.

I've tried the following code:

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

The only thing that I can't get to work is to do a cast from int to targetType so I made it hardcoded to NavigationProjectDates, the enum that I use. And, targetType == NavigationProjectDates...


Edit for more generic Flags Enum converter:

    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;
        }
    }
Coldblooded answered 11/6, 2013 at 10:6 Comment(1)
Somebody had edited my answer to add in the with Flags code, so honestly, I've never tried/used it myself and had considered removing it as I think it makes more sense as its own answer. If I can find some time later, I can try and put something together to test out that code as well as what you have and maybe help come to a better solution for your problem.Radiosensitive
C
0

A TwoWay Binding solution to UWP that takes the usage of Nullable:

C# Part:

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

Here the null value acts as the 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 Part:

...
<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>
Coletta answered 19/3, 2021 at 2:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.