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

DisplayNameAttribute for Enumerations

Rate me:
Please Sign up or sign in to vote.
3.67/5 (12 votes)
21 Aug 2008CPOL3 min read 70.3K   1.1K   45   20
An article about the creation of a DisplayNameAttribute for fields of enumerations in .NET which value will be display in a PropertyGrid.
FieldDisplayNameAttribute.gif

Introduction

The .NET Framework is missing a DisplayNameAttribute class for field members of an enumeration. The DisplayNameAttribute class can be used for class properties but it doesn't support a display name for fields. It is difficult to use the PropertyGrid control for end user dialogs with some meaningful values in a combobox for a property with an enumeration type. There will be shown the original values which are only useful for developers of source code.

Using the Code

First we start with the attribute class. It is pretty simple because we use the .NET class DisplayNameAttribute as the base class which provides all the needed functionality. The only change we have to make is the AttributeTargets which is set to field.

C#
[System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class FieldDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
...

The new attribute class is used by the enumeration which should display nicely.

C#
public enum Driver
{
   /// <summary>
   /// unknown
   /// </summary>

   [FieldDisplayName("Not defined")]
   Unknown,

   /// <summary>
   /// driver for MS SQL Server
   /// </summary>

   [FieldDisplayName(".Net Provider for Microsoft SQL Server")]
   MsSqlClient,
...

At the moment the PropertyGrid control would not recognize the new attribute class and doesn't show the display name. We need a new TypeConverter which can translate between the values of the enumeration and the display names.

C#
/// <summary>
/// TypeConverter for enum types which supports the FieldDisplayName-attribute
/// </summary>

public class EnumTypeConverter : EnumConverter
{
...

The class EnumTypeConverter supports the translation between all types of enumeration and the type string.

C#
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
   //
   // we support converting between string type an enum
   //
   if (sourceType == typeof(string))
      return true;
   return base.CanConvertFrom(context, sourceType);
}

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
   //
   // we support converting between string type an enum
   //
   if (destinationType == typeof(string))
      return true;
   return base.CanConvertTo(context, destinationType);
}

The translation is done by mapping tables which are built once for every enumeration type when it is needed. The mapping tables are build via reflection. The code iterates through the field member of the enumeration and looks for the FieldDisplayNameAttribute. If the attribute is missing the converter uses the ToString() value for mapping purposes.

The second version of the EnumTypeConverter adds support for resource managers. You can provide the display names via resource files for different languages. The definitions in resource files overwrite the value of the FieldDisplayNameAttribute. The key for the resource strings have to be the full type name of the enumeration type followed by "_<enumeration value>". The points within the full type name have to be replaced by underscore. Example: System_Windows_Forms_BorderStyle_None

C#
public override object ConvertTo(ITypeDescriptorContext context,
    System.Globalization.CultureInfo culture, object value, Type destinationType)
{
  object result = value;

   //
   // source value have to be a enumeration type or string, destination a string type
   //
   if (destinationType == typeof(string) &&
       value != null)
   {
      if (value.GetType().IsEnum)
      {
         // ensure that the mapping table is available for the enumeration type
         EnsureMappingsAvailable(value.GetType(), culture);
         MappingContainer container = mappings[value.GetType()];
         MappingPerCulture mapping = container.mappingsPerCulture[culture];
         string valueStr = value.ToString();

         if (mapping.fieldDisplayNameFound)
         {
            if (valueStr.IndexOf(',') < 0)
            {
               // simple enum value
               if (mapping.mappings.ContainsKey(valueStr))
               {
                  result = mapping.mappings[valueStr];
               }
               else
               {
                  throw GetConvertToException(valueStr, destinationType);
               }
            }
            else
            {
               // flag enum with more then one enum value
               string[] parts = valueStr.Split(new string[] { ", " },
                   StringSplitOptions.RemoveEmptyEntries);
               System.Text.StringBuilder builder = new System.Text.StringBuilder();
               string tmp;
               for (int index = 0; index < parts.Length; index++)
               {
                  tmp = parts[index];
                  if (mapping.mappings.ContainsKey(tmp))
                  {
                     builder.Append(mapping.mappings[tmp]);
                     builder.Append(", ");
                  }
                  else
                  {
                     throw GetConvertToException(valueStr, destinationType);
                  }
               }

               builder.Length -= 2;
               result = builder.ToString();
            }
         }
         else
         {
            result = value.ToString();
         }
      }
   }
   else
   {
      result = base.ConvertTo(context, culture, value, destinationType);
   }

   return result;
}

But we also want a combobox in the PropertyGrid control. It doesn't exist if the EnumTypeConverter isn't extended in the right way. For a combobox the PropertyGrid control need some standard values. The TypeConverter support three methods for that problem. The first methods tells generally if the type supports standard values. Of course all enumeration have standard values.

C#
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
   //
   // enumerations support standard values which are a list of the fields of
   // the enumeration
   //
   return true;
}

The second method tells if the values are exclusive values. All enumerations values are exclusive. There will be no other values excepted.

C#
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
   //
   // all standard values of an enumeration are exclusiv values, no others are allowed
   //
   return true;
}

The most important method provides the list of the standard values. The list has to include the original values of the enumeration not the translated display name values. That is important because the translation of the standard values is done in the normal way.

C#
public override TypeConverter.StandardValuesCollection GetStandardValues(
    ITypeDescriptorContext context)
{
   // ensure that the mapping table is available for the enumeration type
   // it builds the standard value collection too
   EnsureMappingsAvailable(EnumType, CultureInfo.CurrentCulture);
   MappingContainer container = mappings[EnumType];
   // wrap it with the right type. it is also possible to use Enum.GetValues
   TypeConverter.StandardValuesCollection values = new StandardValuesCollection(
       container.standardValues);
   return values;
}

Now we have only one thing to do. We have to decorate our enumeration example with an attribute that tells all interested controls and classes to use our type converter class.

C#
[TypeConverter(typeof(EnumTypeConverter))]
public enum Driver
{
...

Since version 2 of this sample you can use the EnumTypeDescriptionProvider. Create an instance and the EnumTypeConverter will be used for every enum type without the need of the TypeConverter attribute.

That's all. A sample dialog with a PropertyGrid control and a sample class which uses the enumeration Driver completes the example.

Conclusion

If you want to use that solution you have only to do the following things:

  • add the classes FieldDisplayNameAttribute and EnumTypeConverter to your project
  • add the attribute [TypeConverter(typeof(EnumTypeConverter))] to your enumeration
  • add the attribute [FieldDisplayName("any meaningful display name")] to the field of your enumeration

For the extended version use the following steps:

  • add all necessary classes to your project
  • create an instance of EnumTypeDescriptionProvider
  • register the ResourceManager by calling the function EnumTypeConverter.RegisterResourceManager

History

Version 2

  • Changed public class EnumTypeConverter : TypeConverter to public class EnumTypeConverter : EnumConverter
  • Fixed support for Flags enumerations
  • Added TypeDescriptor for enumerations which adds support for enumerations without the TypeConverter/FieldDisplayName attribute
  • Added support for resource managers and different languages

Version 1

  • first release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow can I use this project on a Visual Basic project Pin
codemanic30-Jun-14 5:08
codemanic30-Jun-14 5:08 
QuestionWPF Support? Pin
Member 372121220-Feb-12 21:53
Member 372121220-Feb-12 21:53 
GeneralVery nice job, but one question Pin
NQuilles1-Dec-10 23:55
NQuilles1-Dec-10 23:55 
AnswerRe: Very nice job, but one question Pin
MikeCS3-Dec-10 5:37
MikeCS3-Dec-10 5:37 
GeneralRe: Very nice job, but one question Pin
NQuilles4-Dec-10 6:15
NQuilles4-Dec-10 6:15 
AnswerRe: Very nice job, but one question Pin
MikeCS6-Dec-10 5:49
MikeCS6-Dec-10 5:49 
GeneralRe: Very nice job, but one question Pin
NQuilles6-Dec-10 9:01
NQuilles6-Dec-10 9:01 
GeneralRe: Very nice job, but one question Pin
Cleber Movio27-Dec-10 9:25
Cleber Movio27-Dec-10 9:25 
GeneralRe: Very nice job, but one question Pin
MikeCS2-Jan-11 1:07
MikeCS2-Jan-11 1:07 
GeneralRe: Very nice job, but one question Pin
Cleber Movio3-Jan-11 12:24
Cleber Movio3-Jan-11 12:24 
GeneralNormal Convert Pin
alir43116-Aug-10 4:48
alir43116-Aug-10 4:48 
GeneralRe: Normal Convert Pin
MikeCS23-Aug-10 10:32
MikeCS23-Aug-10 10:32 
GeneralMy vote of 5 Pin
Suchan, Lee12-Aug-10 4:02
Suchan, Lee12-Aug-10 4:02 
GeneralRe: My vote of 5 Pin
MikeCS23-Aug-10 9:46
MikeCS23-Aug-10 9:46 
GeneralNot exactly new Pin
Grant Frisken26-Aug-08 12:47
Grant Frisken26-Aug-08 12:47 
GeneralRe: Not exactly new NOT Pin
tonyt10-Dec-08 5:33
tonyt10-Dec-08 5:33 
GeneralThanks. and a suggestion Pin
tonyt25-Jul-08 3:43
tonyt25-Jul-08 3:43 
GeneralRe: Thanks. and a suggestion Pin
MikeCS21-Aug-08 9:22
MikeCS21-Aug-08 9:22 
GeneralWhat? [modified] Pin
PIEBALDconsult24-Jul-08 17:04
mvePIEBALDconsult24-Jul-08 17:04 
GeneralRe: What? Pin
MikeCS24-Jul-08 21:54
MikeCS24-Jul-08 21:54 

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.