Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
SelectListItem Extension Methods for DropDowns in MVC Core. Added ability to specify the option's Group, and for some bug fixes, more XML docs
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&lt;EnumName&gt; myEnums = ExtensionMethods.AsEnumerable&lt;EnumName&gt;();
/// 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;
}
}
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");
}
Owner

nickalbrecht commented Feb 27, 2014

Revised the logic to not depend on strongly typed keys as String and Guid. Will now take any type that exposes a ToString() method.

Owner

nickalbrecht commented Feb 27, 2014

Modified the Enum extension method to correctly handle Enums behaving as Flags (Must have the [Flags] attribute decorating the enum definition).

Owner

nickalbrecht commented Jun 17, 2017

Some bug fixes to the code, added the ability to also select Group, started using this with ASP.NET Core, so there might be some changes there, but I don't believe there were any? Added more XML doc comments and also added the supporting methods I had neglected to include last time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment