Introduction
One of the neat things about enumerations in .NET is that you can use data binding to display and select enum values in a list or drop down combo. For instance, consider the following enum definition:
enum SampleEnum
{
VerySmall,
Small,
Medium,
Large,
VeryLarge
}
We can display the enumeration values (and allow the user to select a value) by adding a ListBox
to a form and setting the DataSource
with one line of code:
_enumListBox.DataSource = Enum.GetValues(typeof(SampleEnum));
That's great - when we run our code now the list box shows the enumeration values:
We can get the selected enum
value by simply using the list box SelectedItem
property:
SampleEnum selectedSampleEnum = (SampleEnum)_enumListBox.SelectedItem;
There are two problems with this technique however. The first problem is that the text we wish to display to the user may not be exactly the same as the enumeration value. In the example above, it would be much nicer to display Very Small
with a space between the words. We can't use this as an enumeration value however because enum
values cannot have spaces. The second problem is that there is no way to translate the enum values if we want to localize our application (i.e. provide a translated user interface for different cultures).
This article presents a simple solution to the above problems that leverages the power of .NET TypeConverter
s.
Background
The article Humanizing the Enumerations by Alex Kolesnichenko also addresses the problems discussed above by attaching a custom attribute to each enumeration value and using an adaptor class (EnumToHumanReadableConverter
) as the data source. Our approach instead leverages the power of the .NET TypeConverter
mechanism to automatically handle conversion to and from localized text and enum
values. The benefit of this approach is that not only does it work for data binding (with no extra code), it can also be used anywhere in your application that you need to convert between localized text and enum
values.
What is a TypeConverter
TypeConverter
s are the in-built .NET mechanism for converting objects of one type to another (for instance from an enum
value to a string
value). When the ListBox
control (and other .NET controls) displays enum
values, it first converts them to string
s using a TypeConverter
. The TypeConverter
it uses depends on the TypeConverter
that has been associated with the type using the System.ComponentModel.TypeConverterAttribute
. By default all enum
types use the predefined EnumConverter
class. As we have seen, the EnumConverter
simply converts the enum
value to its exact string
representation. Fortunately we can define our own derived TypeConverter
class and associate it with our enum
type when we declare it as follows:
[TypeConverter(typeof(LocalizedEnumConverter))]
public enum SampleEnum
{
VerySmall,
Small,
Medium,
Large,
VeryLarge
}
In this solution we define a custom TypeConverter
class (LocalizedEnumConverter
) that converts enum
values to and from string
values using localized string
s from the project resources.
Using the Code
The sample project code consists of a library (Infralution.Localization
) and a separate test application. The library defines a base TypeConverter
class (ResourceEnumConverter
) that converts enum
values to and from string
s using a ResourceManager
that reads the string
values from a compiled RESX file. The ResourceManager
that is used to do the lookup is passed to the constructor of the ResourceEnumConverter
class. Follow the simple steps below to localize enum
s in your application using this class.
Define a class derived from ResourceEnumConverter
in your application project that passes the ResourceManager
for the project RESX file you wish to use for the enum
lookups. Typically you would just use the standard project Properties.Resources
:
class LocalizedEnumConverter : Infralution.Localization.ResourceEnumConverter
{
public LocalizedEnumConverter(Type type)
: base(type, Properties.Resources.ResourceManager)
{
}
}
Use the System.ComponentModel.TypeConverterAttribute
attribute on each enum
declaration to associate this TypeConverter
with the enum
:
[TypeConverter(typeof(LocalizedEnumConverter))]
public enum SampleEnum
{
VerySmall,
Small,
Medium,
Large,
VeryLarge
}
Open the Properties\Resources.resx file in the resources editor and enter the text to display for each enum
value. The resource name is just the enum
type name followed by the value (underscore separated):
Now we are ready to add some localized enum
text values. Create a set of French resources by copying the Properties\Resources.resx file to Properties\Resources.fr.resx. Make sure that the "Custom Tool" (in the Properties window) for this new RESX file is blank - or you will end up with some strange compilation errors. Open the Properties\Resources.fr.resx file in the resources editor and enter the translated values:
Now set the user locale to French using the Control Panel Regional options and run your application. The enum
values are now displayed in French. The sample application demonstrates the above and also allows you to dynamically set the CurrentThread.CurrentCulture
property by selecting it from a drop down list:
The ResourceEnumConverter
class also supports converting text value back to enum
values. The sample application allows you to test this by entering values into the text box and clicking the Convert
button.
Localizing Enums in ASP.NET
In Windows Forms, all of the standard controls use TypeConverters
to convert bound data values to display strings. Unfortunately for some reason Microsoft did not use this same approach when developing ASP.NET controls. ASP.NET controls typically just use the Object.ToString()
method to convert the bound data value to text. This means that while we can still define our TypeConverter
(as described above) it won't be used by default by ASP.NET controls.
To solve this problem we have added a static GetValues
method to the ResourceEnumConverter
class. This method uses the TypeConverter
to return a list of KeyValuePair
objects for the given enum
type. The Key
is the enum
value and the Value
is the localized display text for that enum
. To bind an ASP.NET control we set the DataValueField
property of the control to Key
and the DataTextField
property to Value
. Then we bind the control to the list returned by the GetValues
method as follows:
protected void Page_Load(object sender, EventArgs e)
{
_enumListBox.DataSource =
LocalizedEnumConverter.GetValues(typeof(SampleEnum));
this.DataBind();
}
Flag Enumerations
Andy Mase pointed out that the original code posted did not handle bit field enumerations (defined using the Flag
attribute). In this case an enumeration value can be a bitwise combination of the named enumeration values. The enum may also define named enumeration values which are bitwise combinations of other named values. In the example below an All
value is defined which is the bitwise combination of all other values.
[TypeConverter(typeof(LocalizedEnumConverter))]
[Flags]
public enum TextStyle : byte
{
None = 0x0,
Bold = 0x1,
Italic = 0x2,
Underline = 0x4,
All = 0xFF
}
The ResourceEnumConverter
class now handles converting bit field enum
s to and from text. When converting a bit field enum
value to text, it first checks if the value is one of the named enumeration values. If it is, then the localized text corresponding to the named value will be used. Otherwise it finds the combination of single bit values that are set and creates the text by concatenating the localized text for these together. For example the value 0x3
would be converted to Bold, Italic
in English. The download now includes a separate project (TestLocalizedFlagEnum
) that demonstrates using the LocalizedEnumConverter
with a bit field enum
.
History
- 2007.08.09 - Initial posting
- 2007.10.17 - Added handling of flagged
enum
s and localizing enum
s in ASP.NET