Click here to Skip to main content
Click here to Skip to main content

DisplayNameAttribute for Enumerations

By , 21 Aug 2008
Rate this:
Please Sign up or sign in to vote.
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.

[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.

public enum Driver
{
   /// <span class="code-SummaryComment"><summary></span>
   /// unknown
   /// <span class="code-SummaryComment"></summary></span>

   [FieldDisplayName("Not defined")]
   Unknown,

   /// <span class="code-SummaryComment"><summary></span>
   /// driver for MS SQL Server
   /// <span class="code-SummaryComment"></summary></span>

   [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.

/// <span class="code-SummaryComment"><summary></span>
/// TypeConverter for enum types which supports the FieldDisplayName-attribute
/// <span class="code-SummaryComment"></summary></span>

public class EnumTypeConverter : EnumConverter
{
...

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

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

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.

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.

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.

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.

[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)

About the Author

MikeCS

Germany Germany
No Biography provided

Comments and Discussions

 
QuestionWPF Support? PinmemberMember 372121220-Feb-12 21:53 
GeneralVery nice job, but one question PinmemberNQuilles1-Dec-10 23:55 
AnswerRe: Very nice job, but one question PinmemberMikeCS3-Dec-10 5:37 
GeneralRe: Very nice job, but one question PinmemberNQuilles4-Dec-10 6:15 
AnswerRe: Very nice job, but one question PinmemberMikeCS6-Dec-10 5:49 
GeneralRe: Very nice job, but one question PinmemberNQuilles6-Dec-10 9:01 
GeneralRe: Very nice job, but one question Pinmembercrmmvio27-Dec-10 9:25 
GeneralRe: Very nice job, but one question PinmemberMikeCS2-Jan-11 1:07 
GeneralRe: Very nice job, but one question Pinmembercrmmvio3-Jan-11 12:24 
GeneralNormal Convert Pinmemberalir43116-Aug-10 4:48 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 21 Aug 2008
Article Copyright 2008 by MikeCS
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid