Select Many Enum Values Easily [EnumFlagsSelector WinForms Control]

If you create a WinForms application and wish to allow your users to select many enum values from all enum values, you can use EnumFlagsSelector. EnumFlagsSelector is custom WinForms control that simplifies the creation of user interfaces based on enum types. It makes it easy to choose many enum values from the values of the enum type.

How To Select Many Enum Values With EnumFlagsSelector?

In the article Select one enum value with EnumSelector, we have discussed how to create the WinForms custom control EnumSelector to allow the user to select one enum value from the values of the enum type. Although one can use several EnumSelector instances to achieve this goal, a better solution is to use EnumFlagsSelector.

EnumFlagsSelector is a listbox that displays each value of the enum type (decorated with Flags Attribute) with a checkmark next to it. The user can independently check or uncheck each enum value. When the user checks the value,the value is included in the selection. When the user unchecks the value, The value is excluded from the selection.

To use this custom WinForms control, You should inherit from EnumFlagsSelector with enum type. The enum type should be decorated with Flags attribute.

The Selected property holds the currently selected values. This property is always synchronized with the user interface. In other words, When the user checks or unchecks enum values, the property value is updated to a new selection immediately. When the property is updated in the program – the user interface is updated to reflect the change.

When the user changes its selection by checking or unchecking enum values, The SelectionChanged event is raised. You can use the Selected property of the EnumFlagsSelector, which raised this event, to find out the newly selected value.

By default, the names of the enum values will be displayed in the user interface. While this is very convenient, sometimes this is not suitable (For example, a more detailed name should be displayed, the jargon used to name the values is unfamiliar to the target audience, or the values need to be translated to another language). In this case, You can set the AllowFormat property and override the OnFormat method. This method accepts the enum value to be displayed and should return a string representing the value displayed on the user interface.

What Is The Flags Attribute?

When decorated an enum type with the Flags attribute, we notify the compiler that we want that a variable of this enum should be treated as a bit field. Unlike the default enum semantics, which allows us to store only one enum value (or list of mutually exclusive enum values) in a variable, The Flags attribute will enable us to keep many enum values in a variable. Therefore it can be used to store enum values that might occur in combination.

Here Some guidelines when creating this type of enum:

  • We can use the bit manipulation operators (& for AND , | for OR , ^ for EXCLUSIVE OR) on those values.
  • We should define enum values in powers of 2, that is, 1 (0001b) , 2 (0010b), 4 (0100b), 8 (1000b), and so on. This strategy will give the flags semantics by ensuring that those values can be freely combined using the bitwise OR operation and then be tested for existence using AND operation.
  • You can also define a special value for commonly used combinations by using OR operator on previous declared values.
  • You can also define value 0 as None – This special combination means that no flag is set.

The following demonstrate those guidelines Enum decorated with

Decorating Enum With Flags Attribute
 [Flags]
 enum Sample {
     // special value - No flag set
     N0 = 0,

     // The flags - enum values in powers of 2
     F0 = 1,        // 001b , (F0 & F2) = N0 , (F0 & F1) = N0 , (F0 & F0) = F0
     F1 = 2,        // 010b , (F1 & F2) = N0 , (F1 & F1) = F1 , (F0 & F0) = N0
     F2 = 4,        // 100b , (F2 & F2) = F2 , (F2 & F1) = N0 , (F2 & F0) = N0

     // commonly used combinations of the flags
     C1 = F1 | F0 , // 011b , (C1 & F2) = N0 , (C1 & F1) = F1 , (C1 & F0) = F0
     C2 = F2 | F0 , // 101b , (C2 & F2) = F2 , (C2 & F1) = N0 , (C2 & F0) = F0
     C3 = F2 | F1 , // 110b , (C3 & F2) = F2 , (C3 & F1) = F1 , (C3 & F0) = N0
     C4 = C1 | F2 , // 111b , (C4 & F2) = F2 , (C4 & F1) = F1 , (C4 & F0) = F0 , (C4 & C1) = C1
 }

The FlagsHelper encapsulates common operations on bit fields:

Common Operations On Bit Fields With FlagsHelper
 public static class FlagsHelper {
     // Find whether the set bits in checkBits are also set in flags variable
     public static bool IsSet(this int flags,int checkBits) {
         return ((flags & checkBits) == checkBits);
     }
     //-------------------------------------------------------------------------
     // Set the set bits in setBits in flags variable
     public static void Set(ref int flags,int setBits) {
         flags |= setBits;
     }
     //-------------------------------------------------------------------------
     // Clear the set bits clearBits in flags variable
     public static void Clear(ref int flags,int clearBits) {
         flags &= (~clearBits);
     }
     //-------------------------------------------------------------------------
     // Clear all bits in flags variable
     public static void Clear(ref int flags) {
         flags = 0;
     }
 }

The following program show how can we use this class:

How to use FlagHelper?
 static void Main(string[] args) {
     int xx = 0;                 ; WriteBitField(xx); // 8 => False , 4 => False, 2 => False, 1 => False
     FlagsHelper.Set  (ref xx,8) ; WriteBitField(xx); // 8 =>  True , 4 => False, 2 => False, 1 => False
     FlagsHelper.Set  (ref xx,4) ; WriteBitField(xx); // 8 =>  True , 4 =>  True, 2 => False, 1 => False
     FlagsHelper.Set  (ref xx,1) ; WriteBitField(xx); // 8 =>  True , 4 =>  True, 2 => False, 1 =>  True
     FlagsHelper.Clear(ref xx,4) ; WriteBitField(xx); // 8 =>  True , 4 => False, 2 => False, 1 =>  True
     FlagsHelper.Clear(ref xx,1) ; WriteBitField(xx); // 8 =>  True , 4 => False, 2 => False, 1 => False
     FlagsHelper.Set  (ref xx,2) ; WriteBitField(xx); // 8 =>  True , 4 => False, 2 =>  True, 1 => False
     FlagsHelper.Clear(ref xx  ) ; WriteBitField(xx); // 8 => False , 4 => False, 2 => False, 1 => False
 }

 public static void WriteBitField(int xx) {
     System.Console.WriteLine("8 => {0,5} , 4 => {1,5}, 2 => {2,5}, 1 => {3,5}"
         ,xx.IsSet(8) , xx.IsSet(4), xx.IsSet(2), xx.IsSet(1));
 }

WinForms Demo – Select Many Enum Values

The demo program allows the user to show or hide 4 rectangles with different colors (red, green, yellow, blue). The user can also show or hide all rectangles and show and hide only the red and the blue rectangles. We create DisplayFlagsSelector as EnumFlagsSelector subclass and a new enum DisplayFlags decorated with Flags attribute to implement this feature.

Select Many Enum Values
Select Many Enum Values

DisplayFlags Enum Decorated With Flags attribute

First, we define DisplayFlags enum type with DisplayRed, DisplayGreen, DisplayYellow and DisplayBlue enum values. Please note that those values are powers of 2. We also define DisplayAll and DispleyRedBlue as combination of those flags.

DisplayFlags Enum Decorated With Flags attribute
 [Flags]
 public enum DisplayFlags {
     DisplayRed     = 0x0001,
     DisplayGreen   = 0x0002,
     DisplayYellow  = 0x0004,
     DisplayBlue    = 0x0008,
     DisplayNone    = 0,
     DispleyRedBlue = DisplayRed | DisplayBlue,
     DisplayAll     = DisplayRed | DisplayGreen | DisplayYellow | DisplayBlue,
 }

Now, we create DisplayFlagsSelector with the this DisplayFlags enum. With this custom WinForms control the user can select many enum values of this enum.

DisplayFlagsSelector To Select Many Enum Values Of DisplayFlags
 public partial class DisplayFlagsSelector : EnumFlagsSelector<DisplayFlags> {
 }

When the user update its selection, we update the Visible property of the PictureBoxes.

The handler of the event SelectionChanged of DisplayFlagsSelector
 private void winDisplayFlags_SelectionChanged(object sender,EventArgs ee) {
     winBoxBlue.Visible   = ((int)winDisplayFlags.Selected).IsSet((int)DisplayFlags.DisplayBlue);
     winBoxRed.Visible    = ((int)winDisplayFlags.Selected).IsSet((int)DisplayFlags.DisplayRed);
     winBoxGreen.Visible  = ((int)winDisplayFlags.Selected).IsSet((int)DisplayFlags.DisplayGreen);
     winBoxYellow.Visible = ((int)winDisplayFlags.Selected).IsSet((int)DisplayFlags.DisplayYellow);
 }

The Code Behind EnumSelector

Creating the basic control

To create the control, choose in the project context menu Add > New Item ... > Visual csharp Items > Windows Forms > UserControl. Type the name “EnumFlagsSelector.cs” and click Add. A new design screen of the new user control appears. Drag a CheckedListBox control into the design view and update its properties (Name) to winCheckList and Dock to Fill.

Select Many Enum Values - Create EnumFlagsSelector
Select Many Enum Values – Create EnumFlagsSelector

Adding enum values

As we want this control to display enum values we make it generic with the enum type TT EnumFlagsSelector class

EnumFlagsSelector Inherits From UserControl
 public partial class EnumFlagsSelector<TT> : UserControl {

In the constructor we fill winDropDown with the enum values with help of Enum.GetValues Adding enum values

EnumFlagsSelector – Add Enum Values
 public EnumFlagsSelector() {
     InitializeComponent();
     foreach (object oo in Enum.GetValues(typeof(TT))) {
         if ((int)oo != 0) {
             winCheckList.Items.Add(oo,false);
         }
     }
 }

SelectionChanged event

The SelectionChanged event is raised when the selection is changed. The UpdateSelected will raise this event when the _selected field changed.

The SelectionChanged Event of EnumFlagsSelector
 public event EventHandler SelectionChanged;
     private void UpdateSelected(int selected) {
     if (selected == _selected ) {
         return;
     }
     _selected = selected;
     if (SelectionChanged != null) {
         SelectionChanged(this, new EventArgs());
     }
 }

The Selected property

The Selected property should be synchronized with the user interface:

  • In the property getter will return the _selected field
  • In the property setter – we will update the _selected field, Raise SelectionChanged event. We also updates the user interface. for each checkbox – we will check it if checkbox’s value is setted or uncheck it otherwise.
The Selected property of EnumFlagsSelector
 public TT Selected {
     get {
         return (TT)((object)_selected);
     }
     set {
         int valueInt = (int)((object)value);
         if (_selected == valueInt) {
             return;
         }
         _ignoreCheckEvent = true; // No Item check events while check/unchecking
         for (int ii = 0; ii < winCheckList.Items.Count; ii++) {
             winCheckList.SetItemChecked(ii, FlagsHelper.IsSet(valueInt ,(int)winCheckList.Items[ii]));
         }
         _ignoreCheckEvent = false;
         UpdateSelected(valueInt);
     }
 }
 private bool _ignoreCheckEvent;
 private int _selected = 0;

Synchronize with user interface

To ensure that Selected property getter will always return the selected values in the user interface, we will add a new handler winCheckList_ItemCheck to winCheckList’s ItemCheck event using the properties window.

Select Many Enum Values - ItemCheck Handler
Select Many Enum Values – ItemCheck Handler

In this handler we will update the field to reflect the GUI state and raise our SelectionChanged event.

Synchronize user interface with The Selected property of EnumFlagsSelector
 private void winCheckList_ItemCheck(object sender,ItemCheckEventArgs ee) {
     if (_ignoreCheckEvent) { // will be true when called from selected property setter
         return;
     }
     int selected = 0;
     if (ee.NewValue == CheckState.Checked) {
         FlagsHelper.Set(ref selected, (int)winCheckList.Items[ee.Index]);
     }
     for (int kk = 0; kk < winCheckList.Items.Count; kk++) {
         if ( ee.Index != kk && winCheckList.GetItemCheckState(kk) == CheckState.Checked) {
             FlagsHelper.Set(ref selected, (int)winCheckList.Items[kk]);
         }
     }
     UpdateSelected(selected);
 }

EnumSelector Summary

We see how can we create EnumFlagsSelector – a WinForms custom control to select many enum values. We also create a demo application. With the help of EnumFlagsSelector and minimal lines of code, we could create a specific control that allows to select many values of enum type.

Leave a Reply

Your email address will not be published. Required fields are marked *