Introduction
The design time support in Visual Studio .NET is very good, and makes it very easy to quickly develop forms and user controls. Even so, it has some minor shortcomings - some irritating, and some causing inconvenience. One such shortcoming is the lack of a good way to edit 'flag enum properties' in the design time property browser. Flag enum properties are properties whose type is an enum which has the FlagsAttribute
applied to it. This attribute indicates that the enum can be treated as a bit field; that is, a set of flags. An example of such an enum is the FontStyle
enum. It contains bit values for the Bold, Italic, and Underline styles, which can be combined together if needed. However, the property grid has no inbuilt support for such flag enums - if you want to specify that your font should be Bold as well as Italic, the only way to do so is to write equivalent code.
This article presents a simple UITypeEditor which can be applied to such flag enums and makes it very easy to edit such properties using the design time property browser.
Background
Before discussing the code, it is necessary to give a brief introduction on what an UI Type Editor is. In situations where the simple string-to-value and value-to-string conversion provided by the property browser is not sufficient or convenient, a UI type editor can be used to provide an advanced UI for editing the property. For example, the Dock
and Anchor
properties of all controls use a UI type editor for easy editing of the properties. To specify that a property should use a UI type editor, the EditorAttribute
is applied to the type of the property or to the property itself. In the first case, the UI type editor is used whenever a property has the type to which the attribute is applied. In the second case, the UI type editor is used only for that property.
The FlagEnumUIEditor class
The UI type editor used for flag enums is called FlagEnumUIEditor
, and is derived from UITypeEditor
. It is a dropdown style editor, which means that a small drop-down button is displayed in the property browser when the property is selected. When this button is clicked, a control is shown which can be used to edit the property. The FlagEnumUIEditor
uses a class derived from CheckedListBox
to display the flag enums.
The FlagCheckedListBox class
The FlagCheckedListBox
class is derived from CheckedListBox
and contains items of type FlagCheckedListBoxItem
. Each item has a value and a description for the value corresponding to the values and names of the enum members. The EnumValue
property is used to associate an enum value with the FlagCheckedListBox
:
public Enum EnumValue
{
get
{
object e = Enum.ToObject(enumType,GetCurrentValue());
return (Enum)e;
}
set
{
Items.Clear();
enumValue = value;
enumType = value.GetType();
FillEnumMembers();
ApplyEnumValue();
}
}
The FillEnumMembers
and ApplyEnumValue
functions are as follows:
private void FillEnumMembers()
{
foreach ( string name in Enum.GetNames(enumType))
{
object val = Enum.Parse(enumType,name);
int intVal = (int)Convert.ChangeType(val, typeof(int));
Add(intVal,name);
}
}
private void ApplyEnumValue()
{
int intVal = (int)Convert.ChangeType(enumValue, typeof(int));
UpdateCheckedItems(intVal);
}
protected void UpdateCheckedItems(int value)
{
isUpdatingCheckStates = true;
for(int i=0;i<Items.Count;i++)
{
FlagCheckedListBoxItem item = Items[i] as FlagCheckedListBoxItem;
if( (item.value & value)== item.value && item.value!=0)
SetItemChecked(i,true);
else
SetItemChecked(i,false);
}
isUpdatingCheckStates = false;
}
The OnItemCheck
function of CheckListBox
is overridden to update all other items whenever any item is checked or unchecked:
protected override void OnItemCheck(ItemCheckEventArgs e)
{
base.OnItemCheck(e);
if (isUpdatingCheckStates)
return;
FlagCheckedListBoxItem item = Items[e.Index] as FlagCheckedListBoxItem;
UpdateCheckedItems(item, e.NewValue);
}
protected void UpdateCheckedItems(FlagCheckedListBoxItem composite,
CheckState cs)
{
if(composite.value==0)
UpdateCheckedItems(0);
int sum = 0;
for(int i=0;i<Items.Count;i++)
{
FlagCheckedListBoxItem item = Items[i] as FlagCheckedListBoxItem;
if(GetItemChecked(i))
sum |= item.value;
}
if(cs==CheckState.Unchecked)
sum = sum & (~composite.value);
else
sum |= composite.value;
UpdateCheckedItems(sum);
}
Finally, when the user is done editing the property, the GetCurrentValue
method is used to return the bit value corresponding to all checked items in the CheckListBox
.
public int GetCurrentValue()
{
int sum = 0;
for(int i=0;i<Items.Count;i++)
{
FlagCheckedListBoxItem item =
Items[i] as FlagCheckedListBoxItem;
if( GetItemChecked(i))
sum |= item.value;
}
return sum;
}
Using the code
Using the code in your own project is very easy. Simply add the FlagEnumEditor.cs file to your project, and apply the UI type editor to your enums or properties as follows:
[Editor(typeof(Utils.FlagEnumUIEditor),
typeof(System.Drawing.Design.UITypeEditor))]
The source code zip file contains a Visual Studio .NET solution with a single Windows Application project. To see the FlagEnumUIEditor
in action, simply compile and run this solution. The main form has a property grid which is assigned an object of type TestObject
which is just a dummy type written only for demonstration purposes. TestObject
has three properties, all of type flag enums, namely:
TestEnum
: A flag enum defined for demonstration purposes. Contains enum values which are composites of two other flag members of the enum.
SecurityPermissionFlag
: A flag enum from the System.Security.Permissions
namespace.
FontStyle
: A flag enum from the System.Drawing
namespace.