Click here to Skip to main content
Click here to Skip to main content
Go to top

EnumFlagsSelector - select multiple enum values

, 17 May 2012
Rate this:
Please Sign up or sign in to vote.
How to use EnumFlagsSelector control to select multiple enum values

Screen shot of EnumFlagsSelector

Introduction

In my previous article EnumSelector - Select enum values, I have discussed how to use the custom control EnumSelector to select one constant from all possible constants of enum. Sometimes, you may want the user to select multiple constants from all possible constants of enum. Although one can use multiple EnumSelector instances to achieve this goal, a better solution is to use EnumFlagsSelector - a listbox which display each possible costants of enum, decorated with Flags Attribute, with a check mark next to it.

Using the Control

EnumFlagsSelector control allows to select multiple constants from all possible constants of enum type. To use the control, You should inherit from EnumFlagsSelector with enum type which is decorated with Flags attribute (see related section). Each enum value can be checked to be included in the selection and unchecked to be removed from the selection.

The Selected property holds the current selected constants. This property is always synchronized with the user interface. In other words, When the user check or uncheck enum constants, the property value is updated to new selection immediately and when the property is updated in the program - the user interface is updated to reflect the change.

When the user change its selection by checking or unchecking enum constants SelectionChanged event is raised. You can use the Selected property of the EnumFlagsSelector which raised this event, to find out the new 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 needs to be translated to another language). In this case, You can set AllowFormat property and override OnFormat method. This method accepts the enum constant to be displayed and should return a string representation of the constant which will be displayed on the user interface.

The Flags Attribute

The Flags attribute which can be used to decorate enum, 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 constant (or list of mutually exclusive enum constants) in a variable, The Flags attribute allows us to store multiple enum constants in a variable and therefore can be used to store enum constants that might occur in combination.

Here Some guidelines when creating this type of enum:

  • Define enumeration constants in powers of 2, that is, 1, 2, 4, 8, and so on. This will ensure that individual flags in combined enumeration constants do not overlap. For example, 12 can not be used as constant as its binary representation (1100b) will overlap with 8(1000b) and 4(0100b).
  • You can the bit manipulation operators (& for AND , | for OR , ^ for EXCLUSIVE OR) on those constants

The FlagsHelper is encapsulate common operations on bit fields:

  public static class FlagsHelper {
    // Find whether the setted bits in checkBits are also setted in flags variable
    public static bool IsSet(this int flags,int checkBits) {
      return ((flags & checkBits) == checkBits);
    }    
    //-------------------------------------------------------------------------
    // Set the setted bits in setBits in flags variable
    public static void Set(ref int flags,int setBits) {
      flags |= setBits;
    }
    //-------------------------------------------------------------------------
    // Clear the setted 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
    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));
    }

The Demo program

The demo program allows the user to display or hide 4 rectangles with different colors (Red,Green,Yellow,Blue). In order to achieve this we create DisplayFlagsSelector control which displays 4 checkboxes DisplayRed, DisplayGreen, DisplayYellow and DisplayBlue. To display or hide a rectangle the user should check or uncheck the related checkbox.

To implement this feature the we create a new enum DisplayFlags and a new DisplayFlagsSelector. Please note the Flags Attribute and that we define the enumeration constants in powers of 2.

  [Flags]
  public enum DisplayFlags {
    DisplayRed    = 1,
    DisplayGreen  = 2,
    DisplayYellow = 4,
    DisplayBlue   = 8
  }  
  public partial class DisplayFlagsSelector : EnumFlagsSelector<DisplayFlags> {
  }

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

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

About the control implementation

Creating the basic control

To create the control, choose in the project context menu Add > New Item ... > Visual C# 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.

Adding enum values

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

  public partial class EnumFlagsSelector<TT> : UserControl {

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

    public EnumFlagsSelector() {
      InitializeComponent();
      foreach (object oo in Enum.GetValues(typeof(TT))) {
        winCheckList.Items.Add(oo,false);
      }
    }

The selected property

The Selected property should be synchronized with the user interface - In Selected property setter we check the setted constannts in value and uncheck all other constants. In the property getter will return the _selected field.

    public TT Selected {
      get {
        return (TT)((object)_selected);
      }
      set {
        int valueInt = (int)((object)value);
        if (_selected == valueInt) {
          return;
        }
        _selected = valueInt;
        for (int ii = 0;ii < winCheckList.Items.Count;ii++) {
          winCheckList.SetItemChecked(ii,FlagsHelper.IsSet(_selected,(int)winCheckList.Items[ii]));
        }
      }
    }
    private int _selected = 0;

The SelectionChanged event

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

In this handler we will update the field and raise our SelectionChanged event.

    public event EventHandler SelectionChanged;
    private void winCheckList_ItemCheck(object sender,ItemCheckEventArgs ee) {
      int changedFlag = (int)winCheckList.Items[ee.Index];
      if (ee.NewValue == CheckState.Checked) {
        FlagsHelper.Set(ref _selected,changedFlag);
      } else if ( ee.NewValue == CheckState.Unchecked) {
        FlagsHelper.Clear(ref _selected,changedFlag);
      }
      if (SelectionChanged != null) {
        SelectionChanged(sender,ee);
      }
    }

Update display strings

In order to update names of the values, we add the OnFormat virtual method. We also add new handler winCheckList_Format to winCheckList's Format event which allows to us to convert a constant to its associated display string.

    
    public virtual string OnFormat(TT value) {
      return value.ToString();
    }
    private void winCheckList_Format(object sender,ListControlConvertEventArgs ee) {
      ee.Value = OnFormat((TT)ee.ListItem);
    }

The AllowFormat property connect the handler to the event as requested

    public bool AllowFormat {
      get {
        return _allowFormat;
      }
      set {
        if (value == _allowFormat) {
          return;
        }
        _allowFormat = value;
        if (value) {
          winCheckList.Format += winCheckList_Format;
        } else {
          winCheckList.Format -= winCheckList_Format;
        }
      }
    }

History

  • Version 1 [05.2012]

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

panboza
Software Developer
Israel Israel

I have been developing professionally since 2002.

 

I developed desktop applications in C++ and C#. Now developing web applications and sites in PHP.

 


 
Visit Panboza.

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140905.1 | Last Updated 17 May 2012
Article Copyright 2012 by panboza
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid