SelectListItem Extension Methods for DropDowns in MVC Core. Added ability to specify the option's Group, and for some bug fixes, more XML docs
public static string GetDisplayName<TEnum>(this TEnum enumer) where TEnum : struct
{
var result = null as string;
var iEnumer = Convert.ToInt32(enumer);
if (!typeof(TEnum).GetTypeInfo().IsDefined(typeof(FlagsAttribute), false) || (iEnumer & (iEnumer - 1)) == 0)
{
var display = enumer.GetType()
.GetMember(enumer.ToString()).First()
.GetCustomAttributes(false)
.OfType<DisplayAttribute>()
.LastOrDefault();
result = display != null ? display.GetName() : enumer.ToString().SplitPascalCase();
}
else
{
result = string.Join(", ", GetValues<TEnum>((Enum)(object)enumer).Select(GetDisplayName));
}
return result ?? enumer.ToString().SplitPascalCase();
}
public static IEnumerable<TEnum> GetValues<TEnum>(this Enum value) where TEnum : struct
{
if (!typeof(TEnum).GetTypeInfo().IsEnum)
{
throw new ArgumentException("Generic parameter must be enum");
}
int valueAsInt = Convert.ToInt32(value, System.Globalization.CultureInfo.CurrentCulture);
foreach (object item in Enum.GetValues(typeof(TEnum)))
{
int itemAsInt = Convert.ToInt32(item, System.Globalization.CultureInfo.CurrentCulture);
if (itemAsInt == (valueAsInt & itemAsInt) && itemAsInt != 0)
{
yield return (TEnum)item;
}
}
}
public static IEnumerable<TEnum> AsEnumerable<TEnum>() where TEnum : struct
{
if (!typeof(TEnum).IsEnum)
{
throw new ArgumentException("Generic parameter must be enum");
}
foreach (object item in Enum.GetValues(typeof(TEnum)))
yield return (TEnum)item;
}
public static string SplitPascalCase(this string str)
{
return SplitCamelCase(str);
}
public static string SplitCamelCase(this string str)
{
return Regex.Replace(Regex.Replace(str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2");
}
void Main()
{
}
// Define other methods and classes here
public static class SelectListExtensionMethods
{
/// <summary>
/// The SelectListItem to use by default as the placeholder for any select lists generated by these extension methods.
/// </summary>
public static readonly SelectListItem DefaultEmptySelectListItem = new SelectListItem() { Text = "-- Pick One --", Value = "" };
#region String Keys
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in.
/// </summary>
/// <example>
/// people.ToSelectList(x => x.PersonId, x => x.FullName);
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text) where TType : class
{
return ToSelectList(enumerable, key, text, null);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in.
/// </summary>
/// <example>
/// people.ToSelectList(x => x.PersonId, x => x.FirstName, x => x.LastName);
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="group">The property to use as the group attribute of each list item.</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey, TGroup>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text,
Func<TType, TGroup> group) where TType : class
{
return ToSelectList(enumerable, key, text, group, null);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, with a specific list item selected and optionally an empty list item.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FullName, 2345);
/// // or
/// people.ToSelectList(x => x.PersonId, x => x.FullName, 2345, false); if you don't want the empty list item
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Whether or not a default list item should be the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text, TKey currentKey, bool includeEmptyListItem = true) where TType : class
{
return ToSelectList(enumerable, key, text, currentKey, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, with a specific list item selected and optionally an empty list item.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FirstName, x => x.LastName, 2345);
/// // or
/// people.ToSelectList(x => x.PersonId, x => x.FirstName, x => x.LastName, 2345, false); if you don't want the empty list item
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="group">The property to use as the group attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Whether or not a default list item should be the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey, TGroup>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text, Func<TType, TGroup> group, TKey currentKey, bool includeEmptyListItem = true) where TType : class
{
return ToSelectList(enumerable, key, text, group, currentKey, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, with a specific list item selected and a custom empty list item.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FullName, 2345, new SelectListItem() {Text = "~ Choose ~", Value = ""});
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="emptyListItem">The list item to use as the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text, TKey currentKey, SelectListItem emptyListItem) where TType : class
{
return ToSelectList(enumerable, key, text, new[] { currentKey }, emptyListItem);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, with the provided item as the first/default select list item.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FirstName, x => x.LastName, 2345, new SelectListItem() {Text = "~ Choose ~", Value = ""});
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="group">The property to use as the group attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="emptyListItem">The list item to use as the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey, TGroup>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text, Func<TType, TGroup> group, TKey currentKey, SelectListItem emptyListItem) where TType : class
{
return ToSelectList(enumerable, key, text, group, new[] { currentKey }, emptyListItem);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, allowing for multiple initial items as selected.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FullName, new[] { 2345, 3456 }, false);
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Do you want to use the default option? (default = true)</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text, IEnumerable<TKey> currentKeys, bool includeEmptyListItem = true) where TType : class
{
return ToSelectList(enumerable, key, text, currentKeys, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, allowing for multiple initial items as selected.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FirstName, x => x.LastName, new[] { 2345, 3456 }, false);
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="group">The property to use as the group attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Do you want to use the default option? (default = true)</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey, TGroup>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text, Func<TType, TGroup> group, IEnumerable<TKey> currentKeys, bool includeEmptyListItem = true) where TType : class
{
return ToSelectList(enumerable, key, text, group, currentKeys, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, allowing for multiple initial items as selected, with the provided item as the first/default select list item.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FullName, new[] { 2345, 3456 }, new SelectListItem() {Text = "~ Choose ~", Value = ""});
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Do you want to use the default option? (default = true)</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, string> text, IEnumerable<TKey> currentKeys, SelectListItem emptyListItem) where TType : class
{
var selectList = new List<SelectListItem>();
if (enumerable != null)
selectList = enumerable.Select(x => new SelectListItem()
{
Value = key.Invoke(x).ToString(),
Text = text.Invoke(x),
Selected = (currentKeys != null && currentKeys.Contains(key.Invoke(x)))
})
.ToList();
if (emptyListItem != null)
selectList.Insert(0, emptyListItem);
return selectList;
}
/// <summary>
/// Returns a collection of SelectListItem for each of the items in the collection passed in, allowing for multiple initial items as selected, with the provided item as the first/default select list item.
/// </summary>
/// <example>
/// <code>
/// people.ToSelectList(x => x.PersonId, x => x.FirstName, x => x.LastName, new[] { 2345, 3456 }, new SelectListItem() {Text = "~ Choose ~", Value = ""});
/// </code>
/// </example>
/// <param name="enumerable">The collection to generate the list from</param>
/// <param name="key">The property to use as the value attribute of each list item.</param>
/// <param name="text">The property to use as the text attribute of each list item.</param>
/// <param name="group">The property to use as the group attribute of each list item.</param>
/// <param name="currentKey">The String value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Do you want to use the default option? (default = true)</param>
public static ICollection<SelectListItem> ToSelectList<TType, TKey, TText, TGroup>(this IEnumerable<TType> enumerable, Func<TType, TKey> key, Func<TType, TText> text, Func<TType, TGroup> group, IEnumerable<TKey> currentKeys, SelectListItem emptyListItem) where TType : class
{
var selectList = new List<SelectListItem>();
if (enumerable != null)
{
var groupedEnumerable = enumerable.OrderBy(text.Invoke).GroupBy(group.Invoke).OrderBy(x => x.Key);
foreach (var groupItem in groupedEnumerable.OrderBy(x => x.Key))
{
var selectGroup = new SelectListGroup() { Name = groupItem.Key.ToString() };
selectList.AddRange(groupItem.Select(x => new SelectListItem()
{
Value = key.Invoke(x).ToString(),
Text = text.Invoke(x).ToString(),
Group = selectGroup,
Selected = (currentKeys != null && currentKeys.Contains(key.Invoke(x)))
})
.ToList());
}
}
if (emptyListItem != null)
selectList.Insert(0, emptyListItem);
return selectList;
}
#endregion
#region Enumerable Enums
// The following three methods are only present in case you wish to control how the list if Enum's is built, useful if you need to omit some for security/preference reasons.
// Check out http://www.kodefuguru.com/post/2011/09/21/Empowering-Enums.aspx for a good example of how to do this yourself
public static ICollection<SelectListItem> ToSelectList<TEnum>(this IEnumerable<TEnum> enumerable, bool includeEmptyListItem = true) where TEnum : struct
{
return ToSelectList(enumerable, null, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
public static ICollection<SelectListItem> ToSelectList<TEnum>(this IEnumerable<TEnum> enumerable, SelectListItem emptyListItem) where TEnum : struct
{
return ToSelectList(enumerable, null, emptyListItem);
}
public static ICollection<SelectListItem> ToSelectList<TEnum>(this IEnumerable<TEnum> enumerable, TEnum? currentKey, bool includeEmptyListItem = true) where TEnum : struct
{
return ToSelectList(enumerable, currentKey, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
public static ICollection<SelectListItem> ToSelectList<TEnum>(this IEnumerable<TEnum> enumerable, int currentKey, bool includeEmptyListItem = true) where TEnum : struct
{
TEnum enumCurrentKey = (TEnum)Enum.ToObject(typeof(TEnum), currentKey);
return ToSelectList(enumerable, enumCurrentKey, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem from a provided collection of Enum. Typically you would do this when you have a source enum, but want to premtively exclude certain options.
/// </summary>
/// <example>
/// <code>
/// IEnumerable<EnumName> myEnums = ExtensionMethods.AsEnumerable<EnumName>();
/// myEnums.ToSelectList(currentKey, new SelectListItem() {Text = Resources.Views.Shared.PickOne, Value = ""});
/// </code>
/// </example>
public static ICollection<SelectListItem> ToSelectList<TEnum>(this IEnumerable<TEnum> enumerable, TEnum? currentKey, SelectListItem emptyListItem) where TEnum : struct
{
var selectList = new List<SelectListItem>();
if (enumerable != null)
selectList = enumerable
.Select(x => new SelectListItem() { Value = x.ToString(), Text = x.ToString(), Selected = currentKey != null && (int)(object)x == (int)(object)currentKey })
.ToList();
if (emptyListItem != null)
selectList.Insert(0, emptyListItem);
return selectList;
}
#endregion
/// <summary>
/// Returns a collection of SelectListItem for each possible value of an Enum, with a specific list item selected and optionally an empty list item.
/// </summary>
/// <example>
/// <code>
/// ExtensionMethods.ToSelectList<Colors>(2);
/// // or
/// ExtensionMethods.ToSelectList<Colors>(2, false); if you don't want the empty list item
/// </code>
/// </example>
/// <param name="currentKey">The Guid value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Whether or not a default list item should be the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TEnum>(int currentKey, bool includeEmptyListItem = true) where TEnum : struct
{
TEnum enumCurrentKey = (TEnum)Enum.ToObject(typeof(TEnum), currentKey);
return ToSelectList<TEnum>(enumCurrentKey, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem for each possible value of an Enum, and optionally an empty list item.
/// </summary>
/// <example>
/// <code>
/// ExtensionMethods.ToSelectList<Colors>();
/// // or
/// ExtensionMethods.ToSelectList<Colors>(false); if you don't want the empty list item
/// </code>
/// </example>
/// <param name="includeEmptyListItem">Whether or not a default list item should be the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TEnum>(bool includeEmptyListItem = true) where TEnum : struct
{
return ToEnumSelectList<TEnum>(null, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
public static ICollection<SelectListItem> ToSelectList<TEnum>(SelectListItem defaultListItem) where TEnum : struct
{
return ToEnumSelectList<TEnum>(null, defaultListItem);
}
/// <summary>
/// Returns a collection of SelectListItem for each possible value of an Enum, with a specific list item selected and optionally an empty list item.
/// </summary>
/// <example>
/// <code>
/// // Useful when your enum is a nullable viewmodel
/// Colors? viewModel = null;
/// ExtensionMethods.ToSelectList(viewModel)
/// </code>
/// </example>
/// <param name="currentKey">The Guid value of the list item that should be selected by default.</param>
/// <param name="includeEmptyListItem">Whether or not a default list item should be the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TEnum>(TEnum? currentKey, bool includeEmptyListItem = true) where TEnum : struct
{
return ToEnumSelectList(currentKey, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem for each possible value of an Enum, with a specific list item selected and optionally an empty list item.
/// </summary>
/// <example>
/// <code>
/// Colors.Green.ToSelectList();
/// // or
/// Colors.Green.ToSelectList(false); if you don't want the empty list item
/// // or
/// var color = Colors.Green;
/// color.ToSelectList();
/// </code>
/// </example>
/// <param name="currentKey">Value to mark as Selected</param>
/// <param name="includeEmptyListItem">Whether or not a default list item should be the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TEnum>(this TEnum currentKey, bool includeEmptyListItem = true) where TEnum : struct
{
return ToSelectList(currentKey, includeEmptyListItem ? DefaultEmptySelectListItem : null);
}
/// <summary>
/// Returns a collection of SelectListItem for each possible value of an Enum, with a specific list item selected and a custom empty list item.
/// </summary>
/// <example>
/// <code>
/// Colors.Green.ToSelectList(new SelectListItem() {Text = "~~ Pick One!!! ~~", Value = string.Empty}).Dump();
/// </code>
/// </example>
/// <param name="currentKey">Value to mark as Selected</param>
/// <param name="emptyListItem">The list item to use as the first list item before those from the collection.</param>
public static ICollection<SelectListItem> ToSelectList<TEnum>(this TEnum currentKey, SelectListItem emptyListItem) where TEnum : struct
{
return ToEnumSelectList<TEnum>(currentKey, emptyListItem);
}
private static ICollection<SelectListItem> ToEnumSelectList<TEnum>(TEnum? currentKey, SelectListItem emptyListItem) where TEnum : struct
{
IList<SelectListItem> selectList;
if (typeof(TEnum).GetCustomAttributes(typeof(FlagsAttribute), false).Any())
selectList = Enum.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(x => new SelectListItem()
{
Value = x.ToString(),
Text = x.GetDisplayName(),
Selected = currentKey != null && ((int)(object)x & (int)(object)currentKey) == (int)(object)x
})
.ToList();
else
selectList = Enum.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(x => new SelectListItem()
{
Value = x.ToString(),
Text = x.GetDisplayName(),
Selected = currentKey != null && (int)(object)x == (int)(object)currentKey
})
.ToList();
if (emptyListItem != null)
selectList.Insert(0, emptyListItem);
return selectList;
}
}