Quite frequently in Web and Windows apps, I’ve found it necessary to display data values contained in an Enum type typically inside of a list or combobox type control.
Assume that you want to display a drop down list from this enumeration of bug status types:
public enum BugStatusTypes
{
Entered,
InProcess,
Fixed,
ByDesign,
NotReproducible,
NoBug,
None
}
You can then do something akin to this to turn that enumeration into values to use in the dropdown:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
string[] bugStatuses = Enum.GetNames(typeof(BugStatusTypes));
foreach(string bugStatus in bugStatuses)
{
this.cmbStatus.Items.Add(
new ListItem(StringUtils.FromCamelCase(bugStatus),
bugStatus));
}
}
You can easily retrieve all the enum ‘values’ as strings using the static Enum.GetNames method. This gives you a list of all the enum values. If all you want to do is directly display the enum values as is then you’re done: You can directly bind those enum values to the drop down’s DataSource.
If you want to display the enum values a little more nicely though you might want to fix the values up a little by converting them from Camel Case to more readable strings for display. The code above takes the original list of enum string values and creates a List of objects which hold the actual enum value and a string representation.
The code above creates a list of anonymous types with Text and Value fields that are bound to the drop down. Using the new type is nice
The result of the above looks something like this:
which looks decidedly more user friendly than just the raw enum values.
To make this work the StringUtils.FromCamelCase() function is required which does a rudimentary job of splitting apart camel case text. For kicks ToCamelCase is also listed:
/// <summary>
/// Tries to create a phrase string from CamelCase text.
/// Will place spaces before capitalized letters.
///
/// Note that this method may not work for round tripping
/// ToCamelCase calls, since ToCamelCase strips more characters
/// than just spaces.
/// </summary>
/// <param name="camelCase"></param>
/// <returns></returns>
public static string FromCamelCase(string camelCase)
{
if (camelCase == null)
throw new ArgumentException("Null is not allowed for StringUtils.FromCamelCase");
StringBuilder sb = new StringBuilder(camelCase.Length + 10);
bool first = true;
char lastChar = '\0';
foreach (char ch in camelCase)
{
if ( !first &&
( char.IsUpper(ch) ||
char.IsDigit(ch) && !char.IsDigit(lastChar)) )
sb.Append(' ');
sb.Append(ch);
first = false;
lastChar = ch;
}
return sb.ToString(); ;
}
/// <summary>
/// Takes a phrase and turns it into CamelCase text.
/// White Space, punctuation and separators are stripped
/// </summary>
/// <param name="?"></param>
public static string ToCamelCase(string phrase)
{
if (phrase == null)
return string.Empty;
StringBuilder sb = new StringBuilder(phrase.Length);
// First letter is always upper case
bool nextUpper = true;
foreach (char ch in phrase)
{
if (char.IsWhiteSpace(ch) || char.IsPunctuation(ch) || char.IsSeparator(ch))
{
nextUpper = true;
continue;
}
if (nextUpper)
sb.Append(char.ToUpper(ch));
else
sb.Append(char.ToLower(ch));
nextUpper = false;
}
return sb.ToString();
}
This is a pretty rudimentary implementation of FromCamelCase and ToCamelCase but these come in handy in a number of situations and the enum conversion is a good example.
A little more generic, please
In the above app I actually have quite a few types, so I end up with a fair bit of code that uses selection values from Enum types. I’ve found it useful to create a generic routine that creates me a List of KeyValuePairs that I can use to load up the enum values for databinding:
/// <summary>
/// Returns a List of KeyValuePair object
/// </summary>
/// <param name="enumeration"></param>
/// <returns></returns>
public static List<KeyValuePair<string,string>> GetEnumList(Enum enumeration)
{
string[] enumStrings = Enum.GetNames(enumeration.GetType());
List<KeyValuePair<string,string>> items = new List<KeyValuePair<string,string>>();
foreach(string enumString in enumStrings)
{
items.Add( new KeyValuePair<string,string>(enumString,StringUtils.FromCamelCase(enumString)));
}
return items;
}
which can then be used like this:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
List<KeyValuePair<string, string>> statuses = Utils.GetEnumList(typeof(BugStatusTypes));
this.cmbStatus.DataTextField = "Value";
this.cmbStatus.DataValueField = "Key";
this.cmbStatus.DataSource = statuses;
this.cmbStatus.DataBind();
}
which isn’t really any less code, but maybe a little easier to remember <s>… Notice that you can bind to the Key and Value fields of the KeyValuePair structure. Notice that I used a KeyValuePair object in the list rather than a dictionary. Dictionary would be easier to use but they are a pain to use in data binding and I can never remember what fields you have to bind to for text and value in the data source.
One advantage of this approach too is that you can cache the list and just reuse it in multiple places :
public class App
{
public static BugConfiguration Configuration = null;
public static List<KeyValuePair<string,string>> BugStatusListItems
{
get
{
if ( _bugStatusListItems == null)
_bugStatusListItems = Utils.GetEnumList( typeof(BugStatusTypes));
return _bugStatusListItems;
}
}
private static List<KeyValuePair<string,string>> _bugStatusListItems = null;
static App()
{
Configuration = new BugConfiguration();
}
}
where the App object is a static object that is used to store various configuration/constant information.
Then to bind:
List<KeyValuePair<string, string>> statuses = App.BugStatusListItems;
this.cmbStatus.DataSource = statuses;
this.cmbStatus.DataTextField = "Value";
this.cmbStatus.DataValueField = "Key";
this.cmbStatus.DataBind();
which is easily reusable through an application.
While it’s probably overkill to do this for short simple lists like this, I’m fond of caching repeated lookup lists like this on a static object reference where’s it’s easy to reuse.
Binding Back Enum Data
Once you have a lookup list you and allow selection from it you’ll also need to bind the enum value back to an enum type. I tend to use my own DataBinder control that handles this detail for me automatically, but if you manually do unbinding it’ll look something like this:
Bugs.Entity.Status = Enum.Parse(typeof(BugStatusTypes), this.cmbStatus.SelectedValue);
Remember that the value is the original Enum parsed string and so SelectedValue should retrieve the right value to bind back to whatever object/data item you are binding back to.
Enums as Data?
The whole concept of using Enum values direct as ‘data’ is not something that is appropriate in all situations obviously. In more complex systems this sort of ‘lookup table’ used in the UI is probably better stowed in a database lookup table. And it really only makes sense if your application primarily defines fixed enum values rather than dynamic values that can be added to a table in the database. Using Enums like this is often useful to non-data items like option lists that are not typically part of the data model itself.
However, in simple applications like this bug tracker there’s no separate lookup in the database and creating one just for the purpose of displaying the right value in the UI seems silly.
This concept is also based on the assumption that the Enumeration that’s displayed has enumeration items that are named sensibly and according to .NET Camel Case naming conventions. Obviously if you don’t have reasonably readable Enum values this is not going to produce anything very readable for your users, so be sure that the enum values are appropriate to display as final values. OTOH, I’m a strong believer in using meaningful names in variables and so for me enums almost always have names that could potentially be used in the front end – at least when I created the types…
Finally keep in mind that if localization is important enums are a really bad choice since you can’t localize them in anyway.
As I said at the outset – this is functionality that you’ll likely use only occasionally for internal apps or for a quick input form that needs to be thrown up, but when you do need it’s a nice quick and dirty way to get data to work with into the page.
Related Post:
Enums, Enum Sets, parsing and stuff
Other Posts you might also like