How to bind RadioButtons to an enum?

前端 未结 9 2087
日久生厌
日久生厌 2020-11-22 03:21

I\'ve got an enum like this:

public enum MyLovelyEnum
{
    FirstSelection,
    TheOtherSelection,
    YetAnotherOne
};

I got a property in

9条回答
  •  [愿得一人]
    2020-11-22 04:00

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

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

    
      
        
        Manual
        Automatic
      
      
        
        Sunday
        Monday
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
      
      
        
        Option A
        Option B
      
    
    
    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)

提交回复
热议问题