I've seen information on this technique in several places, possibly even on this forum. I've extended what I've read by creating a couple of classes that make displaying friendly names easier.
First, a bit of background. Enumerations, created in VB with the Enum key word, are lists of text values that each correspond to a number. They are used to provide a limited list of possible values. We use them in many situations, for instance the DialogResult values that are returned by MessageBox.Show are members of the DialogResult enumeration. The idea is that the text labels are descriptive and make life easier for the developer, while the system uses the underlying numeric values for speed and efficiency. Enumerations are better than either String or Integer when you want to limit the possible choices a variable can take.
I say that the text labels are friendly to the developer, but what about the user? The text labels used for enumerated constants must obey the same rules as other identifiers. That means that they must start with a letter or an underscore and can only contain letters, numbers and underscores. They cannot contain spaces or punctuation. That makes them a little less friendly to display to the user in the UI. It would be nice if we could associate a standard String with each value and display those to the user. Well, we can do that, using the DescriptionAttribute class:
vb.net Code:
Imports System.ComponentModel
Public Enum TestEnum
<Description("This is the first value")> FirstValue
<Description("Hello from the second value")> SecondValue
<Description("Third value here")> ThirdValue
End Enum
Note the spaces in the descriptions that are not allowed in the value names themselves. Now, the question is, how do we actually get those descriptions in code? The answer is to use reflection. In other places I've seen this done with a method at the form level or the like. I've been a good little OOPer and created a couple of classes to do the job. The following EnumDescriptor class encapsulates the reflection code, providing access to an enumerated value and its description via properties:
vb.net Code:
Imports System.ComponentModel
Imports System.Reflection
''' <summary>
''' Contains an enumerated constant value and a friendly description of that value, if one exists.
''' </summary>
''' <typeparam name="T">
''' The enumerated type of the value.
''' </typeparam>
Public Class EnumDescriptor(Of T)
''' <summary>
''' The friendly description of the value.
''' </summary>
Private _description As String
''' <summary>
''' The enumerated constant value.
''' </summary>
Private _value As T
''' <summary>
''' Gets the friendly description of the value.
''' </summary>
''' <value>
''' A <b>String</b> containing the value's Description attribute value if one exists; otherwise, the value name.
''' </value>
Public ReadOnly Property Description() As String
Get
Return Me._description
End Get
End Property
''' <summary>
''' Gets the enumerated constant value.
''' </summary>
''' <value>
''' An enumerated constant of the <b>EnumDescriptor's</b> generic parameter type.
''' </value>
Public ReadOnly Property Value() As T
Get
Return Me._value
End Get
End Property
''' <summary>
''' Creates a new instance of the <b>EnumDescriptor</b> class.
''' </summary>
''' <param name="value">
''' The value to provide a description for.
''' </param>
Public Sub New(ByVal value As T)
Me._value = value
'Get the Description attribute.
Dim field As FieldInfo = value.GetType().GetField(value.ToString())
Dim attributes As DescriptionAttribute() = DirectCast(field.GetCustomAttributes(GetType(DescriptionAttribute), _
False), _
DescriptionAttribute())
'Use the Description attribte if one exists, otherwise use the value itself as the description.
Me._description = If(attributes.Length = 0, _
value.ToString(), _
attributes(0).Description)
End Sub
''' <summary>
''' Overridden. Creates a string representation of the object.
''' </summary>
''' <returns>
''' The friendly description of the value.
''' </returns>
Public Overrides Function ToString() As String
Return Me.Description
End Function
End Class
I've also created a class to automatically create an EnumDescriptor for each member of an enumeration and store them as a collection:
vb.net Code:
''' <summary>
''' A collection of EnumDescriptors for an enumerated type.
''' </summary>
''' <typeparam name="T">
''' The type of the enumeration for which the EnumDescriptors are created.
So, there's no need for you to mess around each time you want to do this once you have these classes. It's one line to create a collection containing the enumerated values and their descriptions, which you can bind or use however you like.
I've attached code files for the EnumDescriptor and EnumDescriptorCollection classes.
Last edited by jmcilhinney; Jan 6th, 2009 at 01:57 AM.
Re: [.NET 2.0+] Displaying Friendly Names for Enumerations
Hello. Wondering if you can help me out with a problem I am having implementing your approach for displaying friendly names for enumerations. For a simple ComboBox on a form, everything works ok. My problem is with attempting to add a ComboBox baesd on an enumaration to a bound DataGridView control.
Here are the relevant lines of my code:
Code:
Public Enum Hand
<Description("Left Hand")> LEFT = 1
<Description("Right Hand")> RIGHT = 2
<Description("Two Hands")> TWO_HAND = 3
End Enum
Public Class MyDataGrid
Inherits DataGridView
' ...
' ...
Private Sub FormatColumns
' ...
' ...
' ...
Dim colHand As New DataGridViewComboBoxColumn
With colHand
.DataPropertyName = "HandID" ' Integer field from the database table that stores the value of this enum
.Name = "Hand"
.HeaderText = "Hand"
.DisplayMember = "Description"
.ValueMember = "Value"
.DataSource = New EnumDescriptorCollection(Of Hand)
.FlatStyle = FlatStyle.Standard
End With
.Columns.Add(colHand)
end sub
end class
When I populate the datagrid, it appears to populate OK, but I get an error message for every row that says "System.FormatException: DataGridViewComboBoxCell value is not valid."
Can you provide any guidance on what I might have done wrong, or what might be causing this error? If I remove the ".DataPropertyName" line, then I do not get the error messages, the ComboBox populates just fine, but the value is not displayed.
Re: [.NET 2.0+] Displaying Friendly Names for Enumerations
Originally Posted by GRC1
Hello. Wondering if you can help me out with a problem I am having implementing your approach for displaying friendly names for enumerations. For a simple ComboBox on a form, everything works ok. My problem is with attempting to add a ComboBox baesd on an enumaration to a bound DataGridView control.
Here are the relevant lines of my code:
Code:
Public Enum Hand
<Description("Left Hand")> LEFT = 1
<Description("Right Hand")> RIGHT = 2
<Description("Two Hands")> TWO_HAND = 3
End Enum
Public Class MyDataGrid
Inherits DataGridView
' ...
' ...
Private Sub FormatColumns
' ...
' ...
' ...
Dim colHand As New DataGridViewComboBoxColumn
With colHand
.DataPropertyName = "HandID" ' Integer field from the database table that stores the value of this enum
.Name = "Hand"
.HeaderText = "Hand"
.DisplayMember = "Description"
.ValueMember = "Value"
.DataSource = New EnumDescriptorCollection(Of Hand)
.FlatStyle = FlatStyle.Standard
End With
.Columns.Add(colHand)
end sub
end class
When I populate the datagrid, it appears to populate OK, but I get an error message for every row that says "System.FormatException: DataGridViewComboBoxCell value is not valid."
Can you provide any guidance on what I might have done wrong, or what might be causing this error? If I remove the ".DataPropertyName" line, then I do not get the error messages, the ComboBox populates just fine, but the value is not displayed.
Thanks in advance.
Without having tested, I'm guessing that it's because the column of the table bound to the grid contains Integer values while the ComboBox is exposing Hand values. I don't think the conversion between the two is being done automatically. Try adding this property to your EnumDescriptor class:
Code:
Public ReadOnly Property NumericValue() As Integer
Get
Return CInt(Value)
End Get
End Property
and then set the ValueMember of your column to "NumericValue". If that doesn't work then post back and I'll take a closer look.
Re: [.NET 2.0+] Displaying Friendly Names for Enumerations
Thanks for the quick reply. When I added the property as you suggested the editor gives me an error saying "Value of type 'T' cannot be converted to 'Integer'. I always code with Option Strict on, have I missed something? Thanks!
Re: [.NET 2.0+] Displaying Friendly Names for Enumerations
Originally Posted by GRC1
Thanks for the quick reply. When I added the property as you suggested the editor gives me an error saying "Value of type 'T' cannot be converted to 'Integer'. I always code with Option Strict on, have I missed something? Thanks!
Re: [.NET 2.0+] Displaying Friendly Names for Enumerations
Originally Posted by jmcilhinney
Try Convert.ToInt32 instead of CInt.
I just ran a test and that worked for me. I got the same error message as you when binding Value as the ValueMember but adding the NumericValue property as suggested worked perfectly.