How do I have an enum bound combobox with custom string formatting for enum values?

In the post Enum ToString, a method is described to use the custom attribute DescriptionAttribute like this:

Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

And then, you call a function GetDescription, using syntax like:

GetDescription<HowNice>(NotNice); // Returns "Not Nice At All"

But that doesn't really help me when I want to simply populate a ComboBox with the values of an enum, since I cannot force the ComboBox to call GetDescription.

What I want has the following requirements:

  • Reading (HowNice)myComboBox.selectedItem will return the selected value as the enum value.
  • The user should see the user-friendly display strings, and not just the name of the enumeration values. So instead of seeing "NotNice", the user would see "Not Nice At All".
  • Hopefully, the solution will require minimal code changes to existing enumerations.

Obviously, I could implement a new class for each enum that I create, and override its ToString(), but that's a lot of work for each enum, and I'd rather avoid that.

Any ideas?

Heck, I'll even throw in a hug as a bounty :-)


ComboBox has everything you need: the FormattingEnabled property, which you should set to true, and Format event, where you'll need to place desired formatting logic. Thus,

myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
    {
        e.Value = GetDescription<HowNice>((HowNice)e.Value);
    }

Don't! Enums are primitives and not UI objects - making them serve the UI in .ToString() would be quite bad from a design standpoint. You are trying to solve the wrong problem here: the real issue is that you do not want Enum.ToString() to show up in the combo box!

Now this is a very solveable problem indeed! You create a UI object to represent your combo box items:

sealed class NicenessComboBoxItem
{
    public string Description { get { return ...; } }
    public HowNice Value { get; private set; }

    public NicenessComboBoxItem(HowNice howNice) { Value = howNice; }
}

And then just add instances of this class to your combo box's Items collection and set these properties:

comboBox.ValueMember = "Value";
comboBox.DisplayMember = "Description";

You could write an TypeConverter that reads specified attributes to look them up in your resources. Thus you would get multi-language support for display names without much hassle.

Look into the TypeConverter's ConvertFrom/ConvertTo methods, and use reflection to read attributes on your enum fields.


TypeConverter. I think this is what I was looking for. All hail Simon Svensson!

[TypeConverter(typeof(EnumToStringUsingDescription))]
Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

All I need to change in my current enum is add this line before their declaration.

[TypeConverter(typeof(EnumToStringUsingDescription))]

Once I do that, any enum will get displayed using the DescriptionAttribute of its fields.

Oh, and the TypeConverter would be defined like this:

public class EnumToStringUsingDescription : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return (sourceType.Equals(typeof(Enum)));
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType.Equals(typeof(String)));
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (!destinationType.Equals(typeof(String)))
        {
            throw new ArgumentException("Can only convert to string.", "destinationType");
        }

        if (!value.GetType().BaseType.Equals(typeof(Enum)))
        {
            throw new ArgumentException("Can only convert an instance of enum.", "value");
        }

        string name = value.ToString();
        object[] attrs = 
            value.GetType().GetField(name).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
    }
}

This helps me with my ComboBox case, but obviously doesn't actually override the ToString(). I guess I'll settle for this meanwhile...