enum description value to dropdownlist - c#

I am new to C# and I have a question,
I have a a enum something like
public enum
{
[Description("1,2,3")]
123,
[Description("3,4,5")]
345,
[Description("6,7,8 ")]
678,
}
Now I want the enum descriptions to bind to a dropdownlist.. can some one help me..
thanks in advance!
PS: I am sorry if I am not clear..Let me know if I need to be more specific

public static class EnumExtensionMethods
{
public static string GetDescription(this Enum enumValue)
{
object[] attr = enumValue.GetType().GetField(enumValue.ToString())
.GetCustomAttributes(typeof (DescriptionAttribute), false);
return attr.Length > 0
? ((DescriptionAttribute) attr[0]).Description
: enumValue.ToString();
}
public static T ParseEnum<T>(this string stringVal)
{
return (T) Enum.Parse(typeof (T), stringVal);
}
}
//Usage with an ASP.NET DropDownList
foreach(MyEnum value in Enum.GetValues<MyEnum>())
myDDL.Items.Add(New ListItem(value.GetDescription(), value.ToString())
...
var selectedEnumValue = myDDL.SelectedItem.Value.ParseEnum<MyEnum>()
//Usage with a WinForms ComboBox
foreach(MyEnum value in Enum.GetValues<MyEnum>())
myComboBox.Items.Add(new KeyValuePair<string, MyEnum>(value.GetDescription(), value));
myComboBox.DisplayMember = "Key";
myComboBox.ValueMember = "Value";
...
var selectedEnumValue = myComboBox.SelectedItem.Value;
These two extension methods have been invaluable to me for going on 5 years and two different jobs, for exactly your stated need.

This is how you would write it:
public enum Test
{
[Description("1,2,3")]
a = 123,
[Description("3,4,5")]
b = 345,
[Description("6,7,8")]
c = 678
}
//Get attributes from the enum
var items =
typeof(Test).GetEnumNames()
.Select (x => typeof(Test).GetMember(x)[0].GetCustomAttributes(
typeof(DescriptionAttribute), false))
.SelectMany(x =>
x.Select (y => new ListItem(((DescriptionAttribute)y).Description)))
//Add items to ddl
foreach(var item in items)
ddl.Items.Add(item);

You could build a wrapper class that looks for the DescriptionAttribute on each member and displays that. Then bind to the wrapper instance. Something like this:
Get the Enum<T> value Description

Related

Order by enum description

I am working on an ASP.NET MVC projet using EF code first, and I am facing a situation where I need to order by an enum description:
public partial class Item
{
public enum MyEnumE
{
[Description("description of enum1")]
Enum1,
[Description("description of enum2")]
Enum2,
...
}
public MyEnumE MyEnum { get; set; }
}
Here is the Search and SortAndPaginate functions:
public async Task<IPagedList<Item>> Search(ItemCriteria criteria, SortableTypeE sortName, SortOrder.TypeE sortOrder, int pageNb)
{
var itemFilter = GenerateFilter(criteria);
var items = entities.Items.Where(itemFilter);
return await SortAndPaginate(items, sortName, sortOrder, pageNb);
}
private async Task<IPagedList<Item>> SortAndPaginate(IQueryable<Item> items, SortableTypeE sortName, SortOrder.TypeE sortOrder, int pageNb)
{
IOrderedQueryable<Item> result = null;
switch (sortName)
{
...
case SortableTypeE.Type:
result = sortOrder == SortOrder.TypeE.ASC
? items.OrderBy(i => i.MyEnum.GetDescription())
: items.OrderByDescending(i => i.MyEnum.GetDescription());
result = result.ThenBy(i => i.SomeOtherProperty);
break;
...
}
if (result != null)
{
return await result.ToPagedListAsync(pageNb, 10);
}
return PagedListHelper.Empty<Item>();
}
The problem is that the Item table can be quite huge.
I thought about calling ToListAsync right after entities.Items.Where(itemFilter) but this will get back all filtered items although I only need one page. Does not sound like a good idea.
But if I don't do that EF won't know about GetDescription() mathod and I can only think about two solutions:
- Change my database column to a string (the enum description) instead of the enum itself (but sounds like a hack to me)
- Or alphabetically order MyEnumE components directly in the enum declaration (seems dirty and quite unmaintainable too)
I'm quite stuck since I'm concerned about performances if I call ToListAsync right after filtering, all other solutions seem dirty, and I absolutely need a IPagedList returned from the Search method.
Would anyone have an idea about how to deal with this issue ?
Thanks a lot.
UPDATE
Here is the GetDescription method (can change it if necessary):
public static string GetDescription(this Enum e)
{
FieldInfo fi = e.GetType().GetField(e.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
return attributes[0].Description;
else
return e.ToString();
}
SOLUTIONS
I'll finally go for Ivan Stoev's suggestion because my project is mainly based on Linq (using Linq instead of stored procedures etc.), so this solution seems more suitable to my particular case than creating reference tables.
However Niyoko Yuliawan's and Michael Freidgeim's are also really good answers to me, anyone reading this post and having a more database approach should go for their solutions ;)
Thanks a lot to all of you.
I would go with dynamic expression. It's more flexible and can easily be changed w/o affecting the database tables and queries.
However, instead of sorting by description strings in the database, I would create ordered map in memory, associating int "order" value with each enum value like this:
public static class EnumHelper
{
public static Expression<Func<TSource, int>> DescriptionOrder<TSource, TEnum>(this Expression<Func<TSource, TEnum>> source)
where TEnum : struct
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum) throw new InvalidOperationException();
var body = ((TEnum[])Enum.GetValues(enumType))
.OrderBy(value => value.GetDescription())
.Select((value, ordinal) => new { value, ordinal })
.Reverse()
.Aggregate((Expression)null, (next, item) => next == null ? (Expression)
Expression.Constant(item.ordinal) :
Expression.Condition(
Expression.Equal(source.Body, Expression.Constant(item.value)),
Expression.Constant(item.ordinal),
next));
return Expression.Lambda<Func<TSource, int>>(body, source.Parameters[0]);
}
public static string GetDescription<TEnum>(this TEnum value)
where TEnum : struct
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum) throw new InvalidOperationException();
var name = Enum.GetName(enumType, value);
var field = typeof(TEnum).GetField(name, BindingFlags.Static | BindingFlags.Public);
return field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? name;
}
}
The usage would be like this:
case SortableTypeE.Type:
var order = EnumHelper.DescriptionOrder((Item x) => x.MyEnum);
result = sortOrder == SortOrder.TypeE.ASC
? items.OrderBy(order)
: items.OrderByDescending(order);
result = result.ThenBy(i => i.SomeOtherProperty);
break;
which would generate expression like this:
x => x.MyEnum == Enum[0] ? 0 :
x.MyEnum == Enum[1] ? 1 :
...
x.MyEnum == Enum[N-2] ? N - 2 :
N - 1;
where 0,1,..N-2 is the corresponding index in the value list sorted by description.
Alternative 1
You can do it by projecting enum into custom value and sort by it.
Example:
items
.Select(x=> new
{
x,
Desc = (
x.Enum == Enum.One ? "Desc One"
: x.Enum == Enum.Two ? "Desc Two"
... and so on)
})
.OrderBy(x=>x.Desc)
.Select(x=>x.x);
Entity framework then will generate SQL something like this
SELECT
*
FROM
YourTable
ORDER BY
CASE WHEN Enum = 1 THEN 'Desc One'
WHEN Enum = 2 THEN 'Desc Two'
...and so on
END
If you have a lot of query like this, you can create extension method
public static IQueryable<Entity> OrderByDesc(this IQueryable<Entity> source)
{
return source.Select(x=> new
{
x,
Desc = (
x.Enum == Enum.One ? "Desc One"
: x.Enum == Enum.Two ? "Desc Two"
... and so on)
})
.OrderBy(x=>x.Desc)
.Select(x=>x.x);
}
And call it when you need it
var orderedItems = items.OrderByDesc();
Alternative 2
Another alternative solution is to create additional table that map enum value to enum description and join your table to this table. This solution will be more performant because you can create index on enum description column.
Alternative 3
If you want dynamic expression based on your enum description attribute, you can build yourself
Helper Class
public class Helper
{
public MyEntity Entity { get; set; }
public string Description { get; set; }
}
Get dynamically built expression
public static string GetDesc(MyEnum e)
{
var type = typeof(MyEnum);
var memInfo = type.GetMember(e.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute),
false);
return ((DescriptionAttribute)attributes[0]).Description;
}
private static Expression<Func<MyEntity, Helper>> GetExpr()
{
var descMap = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.ToDictionary(value => value, GetDesc);
var paramExpr = Expression.Parameter(typeof(MyEntity), "x");
var expr = (Expression) Expression.Constant(string.Empty);
foreach (var desc in descMap)
{
// Change string "Enum" below with your enum property name in entity
var prop = Expression.Property(paramExpr, typeof(MyEntity).GetProperty("Enum"));
expr = Expression.Condition(Expression.Equal(prop, Expression.Constant(desc.Key)),
Expression.Constant(desc.Value), expr);
}
var newExpr = Expression.New(typeof(Helper));
var bindings = new MemberBinding[]
{
Expression.Bind(typeof(Helper).GetProperty("Entity"), paramExpr),
Expression.Bind(typeof(Helper).GetProperty("Description"), expr)
};
var body = Expression.MemberInit(newExpr, bindings);
return (Expression<Func<MyEntity, Helper>>) Expression.Lambda(body, paramExpr);
}
Call it like this
var e = GetExpr();
items.Select(e)
.OrderBy(x => x.Description)
.Select(x => x.Entity);
Change my database column to a string (the enum description) instead
of the enum itself (but sounds like a hack to me).
Opposite, for data-driven application it's better to describe Item property in the database reference table MyItemProperty(MyPropKey,MyPropDescription) and have MyPropKey column in your Items table.
It has a few benefits, e.g.
allow to add new property values without need to change code;
allow to write SQL reports having all information in the database without writing c#;
performance optimisation can be done on SQL
level just by requesting one page;
no enum - less code to maintain.
To keep it simple and with a good performance, I would order the enum manually, you only have to do it once, and it will help a lot
public enum MyEnumE
{
Enum1 = 3,
Enum2 = 1,
Enum3 = 2, // set the order here...
}
Here's a simplified example using a join:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication
{
public partial class Item
{
public enum MyEnumE
{
[Description("description of enum1")]
Enum1,
[Description("description of enum2")]
Enum2
}
public Item(MyEnumE myEnum)
{
MyEnum = myEnum;
}
public MyEnumE MyEnum { get; set; }
}
class Program
{
private static IEnumerable<KeyValuePair<int, int>> GetEnumRanks(Type enumType)
{
var values = Enum.GetValues(enumType);
var results = new List<KeyValuePair<int, string>>(values.Length);
foreach (int value in values)
{
FieldInfo fieldInfo = enumType.GetField(Enum.GetName(enumType, value));
var attribute = (DescriptionAttribute)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute));
results.Add(new KeyValuePair<int, string>(value, attribute.Description));
}
return results.OrderBy(x => x.Value).Select((x, i) => new KeyValuePair<int, int>(x.Key, i));
}
static void Main(string[] args)
{
var itemsList = new List<Item>();
itemsList.Add(new Item(Item.MyEnumE.Enum1));
itemsList.Add(new Item(Item.MyEnumE.Enum2));
itemsList.Add(new Item(Item.MyEnumE.Enum2));
itemsList.Add(new Item(Item.MyEnumE.Enum1));
IQueryable<Item> items = itemsList.AsQueryable();
var descriptions = GetEnumRanks(typeof(Item.MyEnumE));
//foreach (var i in descriptions)
// Console.WriteLine(i.Value);
var results = items.Join(descriptions, a => (int)a.MyEnum, b => b.Key, (x, y) => new { Item = x, Rank = y.Value }).OrderBy(x => x.Rank).Select(x => x.Item);
foreach (var i in results)
Console.WriteLine(i.MyEnum.ToString());
Console.WriteLine("\nPress any key...");
Console.ReadKey();
}
}
}
I had a similar problem to solve, only that my ordering had to be dynamic, that is the sort by column parameter is a string.
The boolean sorting also had to be customized in that sense that true comes before false (e.g. 'Active' is before 'Inactive').
I'm sharing here the complete code with you, so you can spare your time. In case you find spots for improvement, please feel free to share in a comment.
private static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> query, SortField sortField)
{
var queryParameterExpression = Expression.Parameter(typeof(T), "x");
var orderByPropertyExpression = GetPropertyExpression(sortField.FieldName, queryParameterExpression);
Type orderByPropertyType = orderByPropertyExpression.Type;
LambdaExpression lambdaExpression = Expression.Lambda(orderByPropertyExpression, queryParameterExpression);
if (orderByPropertyType.IsEnum)
{
orderByPropertyType = typeof(int);
lambdaExpression = GetExpressionForEnumOrdering<T>(lambdaExpression);
}
else if (orderByPropertyType == typeof(bool))
{
orderByPropertyType = typeof(string);
lambdaExpression =
GetExpressionForBoolOrdering(orderByPropertyExpression, queryParameterExpression);
}
var orderByExpression = Expression.Call(
typeof(Queryable),
sortField.SortDirection == SortDirection.Asc ? "OrderBy" : "OrderByDescending",
new Type[] { typeof(T), orderByPropertyType },
query.Expression,
Expression.Quote(lambdaExpression));
return query.Provider.CreateQuery<T>(orderByExpression);
}
The shared GetPropertyExpression has been simplified a bit, to exclude the nested property handling.
private static MemberExpression GetPropertyExpression(string propertyName, ParameterExpression queryParameterExpression)
{
MemberExpression result = Expression.Property(queryParameterExpression, propertyName);
return result;
}
Here is the slightly modified code (from the accepted solution) to handle the Enum ordering.
private static Expression<Func<TSource, int>> GetExpressionForEnumOrdering<TSource>(LambdaExpression source)
{
var enumType = source.Body.Type;
if (!enumType.IsEnum)
throw new InvalidOperationException();
var body = ((int[])Enum.GetValues(enumType))
.OrderBy(value => GetEnumDescription(value, enumType))
.Select((value, ordinal) => new { value, ordinal })
.Reverse()
.Aggregate((Expression)null, (next, item) => next == null ? (Expression)
Expression.Constant(item.ordinal) :
Expression.Condition(
Expression.Equal(source.Body, Expression.Convert(Expression.Constant(item.value), enumType)),
Expression.Constant(item.ordinal),
next));
return Expression.Lambda<Func<TSource, int>>(body, source.Parameters[0]);
}
And the boolean ordering as well.
private static LambdaExpression GetExpressionForBoolOrdering(MemberExpression orderByPropertyExpression, ParameterExpression queryParameterExpression)
{
var firstWhenActiveExpression = Expression.Condition(orderByPropertyExpression,
Expression.Constant("A"),
Expression.Constant("Z"));
return Expression.Lambda(firstWhenActiveExpression, new[] { queryParameterExpression });
}
Also the GetEnumDescription has been modified to receive the Type as the parameter, so it can be called without a generic.
private static string GetEnumDescription(int value, Type enumType)
{
if (!enumType.IsEnum)
throw new InvalidOperationException();
var name = Enum.GetName(enumType, value);
var field = enumType.GetField(name, BindingFlags.Static | BindingFlags.Public);
return field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? name;
}
The SortField is a simple abstraction containing the string column property to be sorted upon and the direction of the sort. For the sake of simplicity I am also not sharing that one here.
Cheers!

Custom Enum Parse

I have an enum as below:
public enum MyEnum { One, Two, Three}
And I would like to pare some strings to above enum, for example, below strings are going to be parsed as MyEnum.Two:
"Two", "TWO", "Second", "2"
I know I can maintain a mapping function to do this job. However, I just want to find a better way, for example, override Enum.Parse function, or something like that. I have tried to use IConvertable, but it seems not possible. Any idea?
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class NameAttribute : Attribute
{
public readonly string[] Names;
public NameAttribute(string name)
{
if (name == null)
{
throw new ArgumentNullException();
}
Names = new[] { name };
}
public NameAttribute(params string[] names)
{
if (names == null || names.Any(x => x == null))
{
throw new ArgumentNullException();
}
Names = names;
}
}
public static class ParseEnum
{
public static TEnum Parse<TEnum>(string value) where TEnum : struct
{
return ParseEnumImpl<TEnum>.Values[value];
}
public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
{
return ParseEnumImpl<TEnum>.Values.TryGetValue(value, out result);
}
private static class ParseEnumImpl<TEnum> where TEnum : struct
{
public static readonly Dictionary<string, TEnum> Values = new Dictionary<string,TEnum>();
static ParseEnumImpl()
{
var nameAttributes = typeof(TEnum)
.GetFields()
.Select(x => new
{
Value = x,
Names = x.GetCustomAttributes(typeof(NameAttribute), false)
.Cast<NameAttribute>()
});
var degrouped = nameAttributes.SelectMany(
x => x.Names.SelectMany(y => y.Names),
(x, y) => new { Value = x.Value, Name = y });
Values = degrouped.ToDictionary(
x => x.Name,
x => (TEnum)x.Value.GetValue(null));
}
}
}
Then you can (note the double syntax for [Name], multiple [Name] or single [Name] with multiple names):
public enum TestEnum
{
[Name("1")]
[Name("Foo")]
[Name("F")]
[Name("XF", "YF")]
Foo = 1,
[Name("2")]
[Name("Bar")]
[Name("B")]
[Name("XB", "YB")]
Bar = 2
}
and
TestEnum r1 = ParseEnum.Parse<TestEnum>("XF");
TestEnum r2;
bool r3 = ParseEnum.TryParse<TestEnum>("YB", out r2);
Note the use of an inner class (ParseEnumImpl<TEnum>) to cache the TEnum "names".
The best way is just store a Dictionary with mappings:
static Dictionary<string, string> _mappings = new Dictionary<string, string>
{
{ "Two", "Two" },
{ "Second", "Two" },
{ "2", "Two" }
};
Then you call case insensetive version of Enum.Parse(Type, String, Boolean):
String str = "2";
MyEnum number = (MyEnum)Enum.Parse(typeof(MyEnum), _mappings[str], true);
Usually, I prefer simple solution since they are much more understandable than"override Enum.Parse function, or something like that".
But we can do it even more simple by using Dictionary<string, MyEnum>:
static Dictionary<string, MyEnum> _mappings = new Dictionary<string, MyEnum>
{
{ "Two", MyEnum.Two },
{ "Second", MyEnum.Two },
{ "2", MyEnum.Two }
};
Now to get your enum:
MyEnum myEnumb = _mappings[str];
The latter approach also improves performance, since we avoid Enum.Parse invocation.
You are trying to parse two distinct cases:
The input contains the enum's name
The input contains the enum's value
If these two are the only cases in the input, you can simply use the Enum.TryParse Method (String, Boolean, TEnum) to try parsing the text in a case-insensitive manner:
MyEnum output;
if (Enum.TryParse(input,true,out output))
{
// Process succesful value
}
C# 7+ Update: The variable can be declared in the same line now:
if (Enum.TryParse(input,true,out var output))
{
// Process succesful value
}
From the documentation examples you see that TryParse can handle both textual and numeric string inputs.
As for parsing Second, this text has no relation to the enum except in the coder's mind. In this case you would really have to create a mapping and place it somewhere - a dictionary, a custom attribute etc.
In fact, if the data comes from external files, this is an ETL problem instead of a parsing problem. In such cases, the typical solution is to create lookup tables that map inputs to recognized outputs and replace the input with the lookup values before parsing
You can use the nuget package Enums.NET to do a reverse lookup on Description Attribute.
Note there's an overload with an ignoreCase boolean.
public enum PhoneCode
{
[DescriptionAttribute("991")]
Emergency,
[DescriptionAttribute("411")]
Info,
}
PhoneCode code;
EnumsNET.Enums.TryParse("991", out code, EnumsNET.EnumFormat.Description)
While I do like the attributes, I went with a mapping myself and instead, I extended the string base type like this:
public enum Rating
{
High,
Medium,
Low,
Other
};
I then have a static class for my extensions... which holds this code in it:
public static Dictionary<string, Rating> RatingsMap = new Dictionary<string, Rating>()
{
{"Highly recommended", Rating.High},
{"Ok", Rating.Medium},
{"Liked", Rating.Medium},
{"Thumbs down", Rating.Low}
};
public static Rating ToRating(this string me)
{
Rating retval = Rating.Other;
if (EXTENSIONS.RatingsMap.ContainsKey(me))
retval = EXTENSIONS.RatingsMap[me];
return retval;
}
Following #xanatos solution. To obtain the EnumValue you can do next:
public static string GetStringValue(Enum value)
{
string output = null;
Type type = value.GetType();
System.Reflection.FieldInfo fi = type.GetField(value.ToString());
NameAttribute[] attrs = fi.GetCustomAttributes(typeof(NameAttribute), false) as NameAttribute[];
if (attrs.Length > 0)
{
output = attrs[0].Name[0];
}
return output;
}

Using an extension to populate a combobox from an enum?

Currently I have the following extension to read the Description attribute from an enum:
public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (attributes.Length > 0) ? (T)attributes[0] : null;
}
Then in my various forms, I have the following code to populate a combobox from an enum:
var list = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
cbo.DataSource = list;
cbo.DisplayMember = "Value";
cbo.ValueMember = "Key";
The above code works fine. However I would like to move the code in a second extension method similar to this:
public static Dictionary<T, string> FromEnum<T>(this ComboBox cbo) where T: struct, IConvertible
{
var list = Enum.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
cbo.DataSource = list;
cbo.DisplayMember = "Value";
cbo.ValueMember = "Key";
}
But the above code doesn't compile. I get the following errors:
Cannot convert lambda expression to type 'System.Collections.Generic.IEqualityComparer' because it is not a delegate type
and
The type or namespace name 'DescriptionAttribute' could not be found (are you missing a using directive or an assembly reference?)
Is it possible have this second extension method working properly?
You can use below code:
public enum SampleEnum
{
[Description("Some Value")]
First = 1,
[Description("Some Value")]
Second = 2,
[Description("Some Value")]
Third = 3,
}
public static IList<ListItem> ToSelectList<T>(this Enum #enum, string firstOption = null)
{
var items = Enum.GetValues(typeof(T)).Cast<T>().ToList<T>();
var list = items.Select(x => new ListItem() { Text = Enum.Parse(typeof(T), x.ToString()).GetDescription(), Value = x.ToString() }).ToList();
if (firstOption != null)
{
list.Insert(0, new ListItem() { Text = firstOption, Value = "" });
}
return list;
}
And use like this:
SampleEnum.First.ToSelectList();
UPDATING
Sorry I forgot to put GetDescrption extension:
public static string GetDescription(this object obj)
{
var fi = obj.GetType().GetField(obj.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
return attributes[0].Description;
return obj.ToString();
}

Generic Enum to SelectList extension method

I need to create a SelectList from any Enum in my project.
I have the code below which I create a select list from a specific enum, but I'd like to make an extension method for ANY enum. This example retrieves the value of the DescriptionAttribute on each Enum value
var list = new SelectList(
Enum.GetValues(typeof(eChargeType))
.Cast<eChargeType>()
.Select(n => new
{
id = (int)n,
label = n.ToString()
}), "id", "label", charge.type_id);
Referencing this post, how do I proceed?
public static void ToSelectList(this Enum e)
{
// code here
}
What I think you are struggling with, is the retrieval of the description. I'm sure once you have those that you can define your final method which gives your exact result.
First, if you define an extension method, it works on a value of the enum, not on the enum type itself. And I think, for easy of usage, you would like to call the method on the type (like a static method). Unfortunately, you cannot define those.
What you can do is the following. First define a method which retrieves the description of the enum value, if it has one:
public static string GetDescription(this Enum value) {
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0) {
description = attributes[0].Description;
}
return description;
}
Next, define a method which takes all values of an enum, and use the previous method to look up the value which we want to show, and return that list. The generic argument can be inferred.
public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>(this TEnum value) {
return Enum
.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(x => new KeyValuePair<TEnum, string>(x, ((Enum)((object)x)).GetDescription()))
.ToList();
}
And finally, for ease of usage, a method to call it directly without value. But then the generic argument is not optional.
public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>() {
return ToEnumDescriptionsList<TEnum>(default(TEnum));
}
Now we can use it like this:
enum TestEnum {
[Description("My first value")]
Value1,
Value2,
[Description("Last one")]
Value99
}
var items = default(TestEnum).ToEnumDescriptionsList();
// or: TestEnum.Value1.ToEnumDescriptionsList();
// Alternative: EnumExtensions.ToEnumDescriptionsList<TestEnum>()
foreach (var item in items) {
Console.WriteLine("{0} - {1}", item.Key, item.Value);
}
Console.ReadLine();
Which outputs:
Value1 - My first value
Value2 - Value2
Value99 - Last one
Late to the party, but since there is no accepted answer and it might help others:
As #Maarten mentioned, an extension method works on the value of an enum, not the enum type itelf, so as with Maarteen's soultion you can create a dummy or default value to call the extension method on, however, you may find, as I did, that it is simpler to just use a static helper method like so:
public static class EnumHelper
{
public static SelectList GetSelectList<T>(string selectedValue, bool useNumeric = false)
{
Type enumType = GetBaseType(typeof(T));
if (enumType.IsEnum)
{
var list = new List<SelectListItem>();
// Add empty option
list.Add(new SelectListItem { Value = string.Empty, Text = string.Empty });
foreach (Enum e in Enum.GetValues(enumType))
{
list.Add(new SelectListItem { Value = useNumeric ? Convert.ToInt32(e).ToString() : e.ToString(), Text = e.Description() });
}
return new SelectList(list, "Value", "Text", selectedValue);
}
return null;
}
private static bool IsTypeNullable(Type type)
{
return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
}
private static Type GetBaseType(Type type)
{
return IsTypeNullable(type) ? type.GetGenericArguments()[0] : type;
}
You would create the select list like this:
viewModel.ProvinceSelect = EnumHelper.GetSelectList<Province>(model.Province);
or using the optional numeric values instead of strings:
viewModel.MonthSelect = EnumHelper.GetSelectList<Month>(model.Month,true);
The basic idea for this I got from here, though I changed it to suit my needs. One thing I added was the ability to optionally use ints for the value. I also added an enum extension to get the description attribute which is based on this blog post:
public static class EnumExtensions
{
public static string Description(this Enum en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
return en.ToString();
}
}
Since enum can't have extensions pinned to the entire collection a convenient way to extend the Enum base class is with a static typed class. This will allow concise code such as:
Enum<MyCustomEnumType>.GetSelectItems();
Which can be achieved with the following code:
public static class Enum<T>
{
public static SelectListItem[] GetSelectItems()
{
Type type = typeof(T);
return
Enum.GetValues(type)
.Cast<object>()
.Select(v => new SelectListItem() { Value = v.ToString(), Text = Enum.GetName(type, v) })
.ToArray();
}
}
Since enum do not have a shared interface Type misuse is possible, but the class name Enum should dispell any confusion.
Here is a corrected [type casted value to int] and simplified [uses tostring override instead of getname] version of Nathaniels answer that returns a List instead of an array:
public static class Enum<T>
{
//usage: var lst = Enum<myenum>.GetSelectList();
public static List<SelectListItem> GetSelectList()
{
return Enum.GetValues( typeof(T) )
.Cast<object>()
.Select(i => new SelectListItem()
{ Value = ((int)i).ToString()
,Text = i.ToString() })
.ToList();
}
}

Convert an enum to List<string>

How do I convert the following Enum to a List of strings?
[Flags]
public enum DataSourceTypes
{
None = 0,
Grid = 1,
ExcelFile = 2,
ODBC = 4
};
I couldn't find this exact question, this Enum to List is the closest but I specifically want List<string>?
Use Enum's static method, GetNames. It returns a string[], like so:
Enum.GetNames(typeof(DataSourceTypes))
If you want to create a method that does only this for only one type of enum, and also converts that array to a List, you can write something like this:
public List<string> GetDataSourceTypes()
{
return Enum.GetNames(typeof(DataSourceTypes)).ToList();
}
You will need Using System.Linq; at the top of your class to use .ToList()
I want to add another solution:
In my case, I need to use a Enum group in a drop down button list items. So they might have space, i.e. more user friendly descriptions needed:
public enum CancelReasonsEnum
{
[Description("In rush")]
InRush,
[Description("Need more coffee")]
NeedMoreCoffee,
[Description("Call me back in 5 minutes!")]
In5Minutes
}
In a helper class (HelperMethods) I created the following method:
public static List<string> GetListOfDescription<T>() where T : struct
{
Type t = typeof(T);
return !t.IsEnum ? null : Enum.GetValues(t).Cast<Enum>().Select(x => x.GetDescription()).ToList();
}
When you call this helper you will get the list of item descriptions.
List<string> items = HelperMethods.GetListOfDescription<CancelReasonEnum>();
ADDITION:
In any case, if you want to implement this method you need :GetDescription extension for enum. This is what I use.
public static string GetDescription(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr =Attribute.GetCustomAttribute(field,typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
/* how to use
MyEnum x = MyEnum.NeedMoreCoffee;
string description = x.GetDescription();
*/
}
In my case, I need to convert it as SelectItem for Checkbox and Radio Button
public class Enum<T> where T : struct, IConvertible
{
public static List<SelectItem> ToSelectItems
{
get
{
if (!typeof(T).IsEnum)
throw new ArgumentException("T must be an enumerated type");
var values = Enum.GetNames(typeof(T));
return values.Select((t, i) => new SelectItem() {Id = i, Name = t}).ToList();
}
}
}

Categories

Resources