Click here to Skip to main content
15,867,292 members
Articles / Programming Languages / C#
Article

Humanizing the Enumerations

Rate me:
Please Sign up or sign in to vote.
4.69/5 (17 votes)
22 Mar 20053 min read 77.8K   863   72   11
Convert enum'eration codes to the phrases your business customers use, with attributes and reflection in .NET.

Foreword

After day 1st being on CodeProject, this article got several interesting comments and I decided to go with Version 2, in order to make it more versatile than Version 1.

Introduction

Say, you have an ol' boring enum reflecting a Purchase Order status in your application's universe:

C#
public enum PurchaseOrderStatus
{
    Opened,
    Consulting,
    InProgress,
    Finalizing,
    Closed,
    Canceled
}

How do you fill the ComboBox with items that communicate in simple terms the business logic behind each of those items? Whicever way you choose, consider the method described below..

Please note that this is more a sample of re-usable code than a tutorial, so I won't go deep into details on how reflection works and such.

Putting the Attributes and Reflection into Business

The approach is simple: each enum's member is being marked with an attribute that links the code with a human term's resource identifier in your application.

Here is how your enum will look like:

C#
public enum PurchaseOrderStatus
{
    Opened,
    Consulting,
    InProgress,
    [HumanReadable("PO_Finalizing")]Finalizing,
    Closed,
    Canceled
}

Looks like nothing has changed. Except that one HumanReadable attribute has been added. But the helper code being described below assumes that you have strings with codes PurchaseOrderStatus.Opened, PurchaseOrderStatus.Consulting and so on, and PO_Finalizing in your resources file then uses reflection and substitutes the codes with human-readable information.

So, the rule is: define your enum; add strings with enum identifiers to your resource file; if there're already strings with such codes, override the codes with HumanReadable attribute; use helper class to convert your enum to human-readable information for data-aware controls.

Since Version 2, you're able not to have resource strings in your resources at all, and the helper code will use HumanReadable attribute for getting the human-readable strings for your enum values. This is helpful when you're developing an application for your internal use and not plannig to translate the values at all. Note that this is not recommended practice.

C#
public enum PurchaseOrderStatus
{
    [HumanReadable("Just Opened")]Opened,
    [HumanReadable("Consulting with Our Partners")]Consulting,
    [HumanReadable("Still in Progress")]InProgress,
    [HumanReadable("Almost There")]Finalizing,
    [HumanReadable("Closed the Deal!")]Closed,
    [HumanReadable("Oops, Canceled")]Canceled
}

This approach doesn't use the standard Description attribute. The purpose of the Description attribute is to show you a detailed description of the method/property/event/whatever in the IDE designer, so better to leave this as is and to not to add additional responsibilities for this attribute.

Now What?

And now just make a call to the helper class method in order to get a map of enum values to strings loaded from resources:

C#
//  Somewhere in the form initialization code...

ComboBox purchaseOrderStatus;

...

//  These can be set from within the IDE
purchaseOrderStatus.DisplayMember = "HumanReadableName";
purchaseOrderStatus.ValueMemeber = "Value";
//-

purchaseOrderStatus.DataSource = 
    EnumToHumanReadableConverter.Convert(typeof(PurchaseOrderStatus), 
    YourResourceManager);

How this Works?

EnumToHumanReadableConverter.Convert method obtains resource string identifiers from the enumeration using reflection and returns an ArrayList of HumanReadableName - Value pairs. ComboBox via its DataSource property consumes this list and uses DisplayMember and ValueMember properties to get a clue of what to display and what to return as SelectedValue.

C#
public class HumanReadablePair
{
    public object Value
    {
        get;
        set;
    }

    public string HumanReadableName
    {
        get;
        set;
    }
}

Actually, you can set DisplayMember and ValueMember properties just from within the IDE, and only assign DataSource property in your code.

In order to select a proper value in the box, you can use the following snippet:

C#
purchaseOrderStatus.SelectedValue = PurchaseOrderStatus.Finalizing;

The following code will retrieve the selected Purchase Order status from the ComboBox:

C#
PurchaseOrderStatus selected = 
         (PurchaseOrderStatus)purchaseOrderStatus.SelectedValue;

YourResourceManager variable is the instance of the resource manager that will be used to load proper strings depending on their identifiers from the resources your application uses. The variable can be initialized once and be used in many places of your application. Such an approach makes the code globalization-aware.

C#
ResourceManager YourResourceManager = 
    new ResourceManager("HumanizingTheEnumerations.Strings", 
    Assembly.GetExecutingAssembly());

Here's how your resource editor will look like:

Image 1

Once again, the code that shows the enum values in ComboBox:

C#
purchaseOrderStatus.DataSource = 
    EnumToHumanReadableConverter.Convert(typeof(PurchaseOrderStatus), 
    YourResourceManager);

If you do not plan to localize the UI of your application, you can use a simpler version of the Convert method:

C#
purchaseOrderStatus.DataSource = 
    EnumToHumanReadableConverter.Convert(typeof(PurchaseOrderStatus));

Since Version 2, you're able to obtain human-readable string for a particular value of the enum:

C#
labelTest.Text = 
    EnumToHumanReadableConverter.StringFor(InvoiceStatus.Processing, 
    YourResourceManager);

Or

C#
labelTest.Text = 
    EnumToHumanReadableConverter.StringFor(InvoiceStatus.Processing);

All you need to start with the described approach is in the HumanReadableAttribute.cs file attached to the article.

Just Like That

Image 2

Did I mention that it also works with ListBox? With all data-aware controls, I guess.

History

  • Version 2.0 so far.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
Started professional career in software development back in 2000, in Ukraine. Founder and owner of a boutique software company called ByteGems.com Software. Worked for 6 years at w2bi, Inc in New Jersey USA, currently work in a large multinational company based in Redmond, WA.

My buzzwords at the moment: .NET, C#, ASP.NET, MVC, LINQ, TypeScript, JavaScript, AngularJS, HTML, JSON, services.

Still buzzing: C++, Win32, ATL, MFC, SQL, WinForms, WebForms, EF, Sockets, TCP/IP, Remoting.

Comments and Discussions

 
GeneralTypeConverter for Enum used in a Combobox + Datagrid Pin
jimbutler10116-Nov-06 0:56
jimbutler10116-Nov-06 0:56 
GeneralYou should use a custom TypeConverter Pin
Daniel Cazzulino [XML MVP]31-Mar-05 4:35
Daniel Cazzulino [XML MVP]31-Mar-05 4:35 
GeneralRe: You should use a custom TypeConverter Pin
Alex Kolesnichenko1-Apr-05 7:08
professionalAlex Kolesnichenko1-Apr-05 7:08 
AnswerRe: You should use a custom TypeConverter Pin
ellarr1-Dec-06 13:38
ellarr1-Dec-06 13:38 
GeneralRe: You should use a custom TypeConverter [modified] Pin
Grant Frisken9-Aug-07 17:53
Grant Frisken9-Aug-07 17:53 
QuestionWhy use a string table? Pin
Anonymous21-Mar-05 7:15
Anonymous21-Mar-05 7:15 
AnswerRe: Why use a string table? Pin
Alex Kolesnichenko21-Mar-05 7:49
professionalAlex Kolesnichenko21-Mar-05 7:49 
GeneralLocalization is nice, but use DescriptionAttribute Pin
Marc Brooks21-Mar-05 5:43
Marc Brooks21-Mar-05 5:43 
There's already an attribute for this which you can use or inherit from. Keep the helper method though, nice localization! One question... if you're never going to display the helper string, which not simply use the full-name of the enumeration and element as the resource key? Then you don't need any attribute, just try the translate and if you don't find the resource, return the enum's name untouched or "sentance-ize" it using code like this:
public static string DisplayableEnumeration(System.Enum enumValue)
{
	string display = enumValue.ToString();

	int start = 1;
	while (true)
	{
		int capital = display.IndexOfAny(s_Capitals, start);

		if (capital > 0)
		{
			display = display.Substring(0, capital) + " " + display.Substring(capital);
			start = capital + 2;
		}
		else
		{
			return display.Trim();
		}
	}
}
which works like this
private enum MyTestEnum { Capital, TwoCapitals };
[Test]
public void DisplayableEnumerations()
{
	Assert.AreEqual("Capital", DisplayableEnumeration(MyTestEnum.Capital));
	Assert.AreEqual("Two Capitals", DisplayableEnumeration(MyTestEnum.TwoCapitals));
}

GeneralRe: Localization is nice, but use DescriptionAttribute Pin
Richard Poole21-Mar-05 6:31
Richard Poole21-Mar-05 6:31 
GeneralHeath's solution Pin
Uwe Keim20-Mar-05 21:00
sitebuilderUwe Keim20-Mar-05 21:00 
GeneralRe: Heath's solution Pin
Alex Kolesnichenko20-Mar-05 23:45
professionalAlex Kolesnichenko20-Mar-05 23:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.