Sacha posted a nice article on how to get friendly names for enums. I’ve seen this kind of approach before using the Description attribute. One of the things that he does though is create a static method to obtain the list of enum members. You can however actually get the list with pure xaml as follows:
<ObjectDataProvider MethodName=”GetValues”
ObjectType=”{x:Type sys:Enum}”
x:Key=”DayOfWeekValues”>
<ObjectDataProvider.MethodParameters>
<x:Type TypeName=”sys:DayOfWeek” />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
And then you can wire it up to a combobox:
<ComboBox x:Name=”cboDayOfWeek”
ItemsSource=”{Binding Source={StaticResource DayOfWeekValues}}” />
The only problem though is that its verbose (as well as a little heavy for something that is just meant to return a static list). When I write a test app for a control I’m writing I tend to end up having a lot of these and it ends up cluttering the xaml. I was thinking about it and an alternative approach which combines the above as well as takes Sacha’s issue into account would be to write a markup extension that combines all this together.
/// <summary>
/// Markup extension that provides a list of the members of a given enum.
/// </summary>
public class EnumListExtension : MarkupExtension
{
#region Member Variables
private Type _enumType;
private bool _asString;
#endregion //Member Variables
#region Constructor
/// <summary>
/// Initializes a new <see cref=”EnumListExtension”/>
/// </summary>
public EnumListExtension()
{
}
/// <summary>
/// Initializes a new <see cref=”EnumListExtension”/>
/// </summary>
/// <param name=”enumType”>The type of enum whose members are to be returned.</param>
public EnumListExtension(Type enumType)
{
this.EnumType = enumType;
}
#endregion //Constructor
#region Properties
/// <summary>
/// Gets/sets the type of enumeration to return
/// </summary>
public Type EnumType
{
get { return this._enumType; }
set
{
if (value != this._enumType)
{
if (null != value)
{
Type enumType = Nullable.GetUnderlyingType(value) ?? value;
if (enumType.IsEnum == false)
throw new ArgumentException(“Type must be for an Enum.”);
}
this._enumType = value;
}
}
}
/// <summary>
/// Gets/sets a value indicating whether to display the enumeration members as strings using the Description on the member if available.
/// </summary>
public bool AsString
{
get { return this._asString; }
set { this._asString = value; }
}
#endregion //Properties
#region Base class overrides
/// <summary>
/// Returns a list of items for the specified <see cref=”EnumType”/>. Depending on the <see cref=”AsString”/> property, the
/// items will be returned as the enum member value or as strings.
/// </summary>
/// <param name=”serviceProvider”>An object that provides services for the markup extension.</param>
/// <returns></returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (null == this._enumType)
throw new InvalidOperationException(“The EnumType must be specified.”);
Type actualEnumType = Nullable.GetUnderlyingType(this._enumType) ?? this._enumType;
Array enumValues = Enum.GetValues(actualEnumType);
// if the object itself is to be returned then just use GetValues
//
if (this._asString == false)
{
if (actualEnumType == this._enumType)
return enumValues;
Array tempArray = Array.CreateInstance(actualEnumType, enumValues.Length + 1);
enumValues.CopyTo(tempArray, 1);
return tempArray;
}
List<string> items = new List<string>();
if (actualEnumType != this._enumType)
items.Add(null);
// otherwise we must process the list
foreach (object item in Enum.GetValues(this._enumType))
{
string itemString = item.ToString();
FieldInfo field = this._enumType.GetField(itemString);
object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (null != attribs && attribs.Length > 0)
itemString = ((DescriptionAttribute)attribs[0]).Description;
items.Add(itemString);
}
return items.ToArray();
}
#endregion //Base class overrides
}
The markup extension has a Type property that can be used to indicate the enum type for which it should obtain the list. If you want it to return strings as opposed to the actual enum values and take the Description attribute into account, you just set the AsString property to true. So now the hookup is much simpler:
<ComboBox x:Name=”cboDayOfWeek”
ItemsSource=”{Binding Source={local:EnumList {x:Type sys:DayOfWeek}}}” />
Note: I updated this code to use the DisplayName instead of the DescriptionAttribute after Mike Brown correctly pointed out that DisplayName is a more appropriate attribute for this scenario.
Updated: I reverted back to using the Description attribute since the DisplayNameAttribute’s usage does not allow its use on Fields. Thanks to John for pointing this out.