TP

Universal enumeration editor control

Introduction

At first I'd like to say that this control exists in two versions. First version is windows forms user control (namespace EeekSoft.­WinForms.­Controls) and second is web control that can be used from Asp.Net applications (namespace EeekSoft.­Web.­Controls). Each control is compiled to single independent dll file, so you can use first assembly in win forms application and second in web application. Both controls has same (or very similar) functionality so I decided to put this two controls in one article.

This controls can be used to allow user to enter value of any enumeration data type (in C# it is enum and in VB.Net Enum data type). This is possible thanks to reflections, because using reflection control can get all enumeration values and its names. One problem is that enumeration can be used for selecting one value from specified options or to store data as bitwise flags and the control must be able to edit both of this types. Also there are some other usefull features that will be discussed later.

Asp.Net Control screenshotWinForms Control screenshot

Background

This control generates set of RadioButton or CheckBox (when enum allows to choose more values) controls. You can specify this using EditorType property, which can have one of following values. Flags means that user can choose more values (CheckBoxes), Options means that user can choose only one value (RadioButtons) and third value is Auto. In the third case control checks whether enumeration is marked using FlagsAttribute attribute (this is standard attribute used with enumerations) and behaves as Flags editor or Options editor automaticly.

Reflection

Thanks to reflection it is possible to edit any kind of enumeration using this controls. First interesting thing that I'd like to discuss is how to get list of all values of enum. Following code will first get System.Type object of enumeration using typeof operator. Than it gets all its fields using GetFields method. This method returns array of all fields of type, so it is simple to loop through it and find all acceptable fields.

// First we need to get list of all enum fields
FieldInfo[] fields=typeof(MyEnum).GetFields();
foreach(FieldInfo field in fields)
{
  // Continue only for normal fields
  if (field.IsSpecialName) continue;

  // Parameter should be instance of object, but it is ignored
  // for static fileds (this case) so it is not important
  object valObj=field.GetValue(0));
  // Convert value to integer - this works only for enums which use Int32
  // for storing data (in control there is block of try-catch) 
  int val=(int)valObj;
  
  // Add control with specified value and title
  AddControl(val,field.Name);
}

Attributes

I also used attributes to control behavior of controls. I already mentioned attribute FlagsAttribute thath is used for determinating enumeration type. Other used attributes are from namespace System.ComponentModel. First attribute (BrowsableAttribute) is used to choose which values of enum should be user able to select and second (DescriptionAttribute) allows you to add description for each value.

Following code shows how to find out if field should be hidden (it is hidden when it is marked with attribute Browsable(false) and get description of value. It uses method GetCustomAttributes, of FieldInfo which returns array with information about attributes of specified type.

// Array used for retrieving attributes
object[] attrs;

// Get array of BrowsableAttribute attributes
attrs=field.GetCustomAttributes(typeof(BrowsableAttribute),false);
if (attrs.Length==1)
{
  // If attribute exists and its value is false continue to the next field...
  BrowsableAttribute brAttr=(BrowsableAttribute)attrs[0];
  if (brAttr.Browsable==false) continue;
}

// Additional description for the field
string description="";
// Get DescriptionAttribute associated with the filed (if exists)
attrs=field.GetCustomAttributes(typeof(DescriptionAttribute),false);
if (attrs.Length>0) 
{
  description=((DescriptionAttribute)attrs[0]).Description;
}

How to use this controls

Both controls work similar, but there are some little differences that will be discussed later. In all examples I will use following two enumerations (First is marked with FlagsAttribute, so user can select more than one flags - control will generate CheckBoxes and second enum can be used to select only one of three values, so editor will contain RadioButtons):

// This enum is used for demonstration of flags editor functionality
[Flags]
enum MyFlagsEnum
{
  // Hidden fields (will not be displayed in editor)
  [Browsable(false)] None=0,
  [Browsable(false)] BothFlags=3,

  [Description("First flag")]  FirstFlag=1,
  [Description("Second flag")] SecondFlag=2,
  [Description("Third flag")]  ThirdFlag=4
}

// This enum is used for demonstration of options editor functionality
enum MyOptionsEnum
{
  [Description("First option")]  FirstOpt=1,
  [Description("Second option")] SecondOpt=2,
  [Description("Third option")]  ThirdOpt=3
}

WinForms control

If you want to add this control to your form you can use designer (If you want to add control to your toolbox click on 'Add/Remove items' in toolbox context menu and select assembly with control). You can modify only properties which affect control look in designer and the rest (enumeration type) can be changed only from code. ControlSpacing property specifies vertical offset of generated controls. LabelFormat property allows you to set format of text labels. You can use two parameters: {0} specifies name of value and {1} is description stored in DescriptionAttribute attribute. As I said before, you can override default controls behavior. If you want to use it as bitwise flags editor, set EditorType to Flags) and for choosing one option use Options. One thing, that can't be done using designer is to set type of enumeration (property EnumType as you can see in following example. Control has one event called Change that is raised when value of enumeration changes.

// Initialize enumeration editors
private void TestForm_Load(object sender, System.EventArgs e)
{
  // Flags editor
  myFlagsEditor.EnumType=typeof(MyFlagsEnum);
  myFlagsEditor.EnumValue=(long)
    (MyFlagsEnum.FirstFlag | MyFlagsEnum.SecondFlag);
	
  // Options editor
  myOptsEditor.EnumType=typeof(MyOptionsEnum);
  myOptsEditor.EnumValue=(long)MyOptionsEnum.ThirdOpt;
}

Asp.Net control

Asp.Net version of control is very similar to WinForms control. After you add control to toolbox you can put it on the web form and set it's design properties using web forms designer, but as in WinForms version you must set type of edited enum manually (for example in Page_Load event handler).

Only one important difference between properties of WinForms and Asp.Net controls is that Asp.Net version doesn't have a ControlSpacing property (because I think it is better not to use absolute positioning in web applications for this). Instead of this, you can use ControlSeparator property to specify HTML tags which will control put between every two generated RadiButtons or CheckBoxes. The default value for this property is <br />, but if you prefer to use table layout you can simply modify it. Also there is little modification in event model. Property AutoPostBack allows you to set when page postback should occur. If you set it to true page will do postback (and Change event will be called) whenever the value is changed and if you set it to false control will never raise postback and Change event will be called after page is sent back to the server for some other reason.

Following code sample shows how to add enum editor control to web form and how to use it from codebehind (code in cs file presumes, that you set event handler for Change event in designer):

<!-- EnumEditorDemo.aspx -->
<%@@ Page language="c#" Codebehind="EnumEditorDemo.aspx.cs" 
    Inherits="WebFormsDemo.EnumEditorDemo" %>
<%@@ Register TagPrefix="ec" Namespace="EeekSoft.Web.Controls"
    Assembly="EeekSoft.Web.EnumEditor" %>
...
  <ec:enumeditor id="myFlagsEditor" LableFormat="{0} - {1}" 
      runat="server" ControlSeparator="<br />"></ec:enumeditor>
  <asp:label id="valueLabel" runat="server"><asp:label>
...
// EnumEditorDemo.aspx.cs
private void Page_Load(object sender, EventArgs e)
{
  if (!Page.IsPostBack)
  {
    // Set edited enumeration type and default value
    myFlagsEditor.EnumType=typeof(MyFlagsEnum);
    myFlagsEditor.EnumValue=(long)
      (MyFlagsEnum.FirstFlag | MyFlagsEnum.SecondFlag);
  }
}

// Value of edited enumeration changed
private void myFlagsEditor_Change(object sender, EventArgs e)
{
  // Show name of selected value using label
  valueLabel.Text=((MyFlagsEnum)myFlagsEditor.EnumValue).ToString();
}

Who can use it?

I think that both Asp.Net and WinForms versions of control can be very usefull if you want to allow user to modify settings of your application and especially if you store this settings in enumerations. Typical use case may be when you load integer containing value of enumeration from database and you want to allow user to simply change its value and save it back to database. In this case you can write lot of code and add lot of CheckBox and RadioButton controls or you can use this control and define structure of data very simply and clearly using enum (and this solution is also very easy to maintain).

Published: Friday, 31 December 2004, 3:54 AM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: