A Better SelectList for ASP.Net MVC (Revisited)

Some time ago I wrote up a set of overloaded methods to help build a select list from an enumerable object. I’ve revisited the code and made it perhaps more simpler and less prone to exception. The revised code is found at the end of this post.

The following is a more complete version of the code I posted as an answer to a Stack Overflow question. The purpose is to provide a statically typed method for creating a SelectList. If you work with ASP.NET MVC, you will have encountered these. This version of the code has more overloads to make it that much easier to set selected values, which is especially handy when using a multiple-selection drop-down box. You can specify a boolean to make all options selected or not, or pass a single value or an array of values that correspond to the items you want selected.

The benefit of making it statically typed is that you’ll catch errors at design-time that would have otherwise occurred at run-time. To create a SelectList previously required passing string values representing property names that could be incorrect or misspelled, which goes unnoticed by the compiler. An error would not occur until a user tried to view your page.

The complete ToSelectList method listings follow the usage examples because it is quite lengthy.

Usage example in a controller action:

var allCompanies = new[] { "ABC", "IBM", "XYZ" };
var allLocations = new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("LA", "Los Angeles"), new KeyValuePair<string, string>("NY", "New York"), new KeyValuePair<string, string>("POR", "Portland"), new KeyValuePair<string, string>("SEA", "Seattle") };
var allJobs = new Dictionary<int, string>() { { 500, "Manager" }, { 600, "Supervisor" }, { 700, "Lead" } };

var myCompany = "XYZ";
var myLocations = new string[] { "LA", "POR" };

ViewBag.CompanyList = allCompanies.ToSelectList(x => x, myCompany /* selectedValue */);
ViewBag.LocationList = allLocations.ToSelectList(x => x.Key, x => x.Value, myLocations /* selectedValues */);
ViewBag.JobList = allJobs.ToSelectList(x => x.Key, x => x.Value, true /* selectAll */);

And subsequent usage in a view (razor syntax):

A regular select element with options:<br />
@Html.DropDownList("company", ViewBag.CompanyList as IEnumerable<SelectListItem>, "(Select one)")<br />

Select elements allowing multiple selections:<br />
@Html.ListBox("locations", ViewBag.LocationList as IEnumerable<SelectListItem>)<br />
@Html.ListBox("jobs", ViewBag.JobList as IEnumerable<SelectListItem>, new { size = "3" })

This would result in one normal select element and two select elements allowing multiple selections. The first, “company”, would have “XYZ” selected. (If companies don’t exist for one or more of the given values, it is simply skipped and no error is thrown–a good thing.) The second, “locations”, has two of the four items selected in a multiple selection element. The third, “jobs”, would initially have all of the values in the list selected since we passed true for the selectAll parameter.

ToSelectList.cs with inline documentation for each overloaded method.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

/// <summary>
/// Partial class for helper methods
/// </summary>
public static partial class Helpers
{
    /// <summary>
    /// Creates an IEnumerable&lt;SelectListItem&gt; from an IEnumerable&lt;T&gt;.
    /// </summary>
    /// <typeparam name="T">The type of the elements of enumerable.</typeparam>
    /// <param name="enumerable">The IEnumerable&lt;T&gt; to create an IEnumerable&lt;SelectListItem&gt; from.</param>
    /// <param name="value">A function to extract a value from each element.</param>
    /// <param name="selectAll">Whether all values are selected.</param>
    /// <returns>An IEnumerable&lt;SelectListItem&gt; that contains elements from the IEnumerable&lt;T&gt;.</returns>
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, bool selectAll = false)
    {
        return enumerable.ToSelectList(value, value, selectAll);
    }

    /// <summary>
    /// Creates an IEnumerable&lt;SelectListItem&gt; from an IEnumerable&lt;T&gt;.
    /// </summary>
    /// <typeparam name="T">The type of the elements of enumerable.</typeparam>
    /// <param name="enumerable">The IEnumerable&lt;T&gt; to create a IEnumerable&lt;SelectListItem&gt; from.</param>
    /// <param name="value">A function to extract a value from each element.</param>
    /// <param name="selectedValue">Items matching this value will be initially selected.</param>
    /// <returns>An IEnumerable&lt;SelectListItem&gt; that contains elements from the IEnumerable&lt;T&gt;.</returns>
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, object selectedValue)
    {
        return enumerable.ToSelectList(value, value, new List<object>() { selectedValue });
    }

    /// <summary>
    /// Creates an IEnumerable&lt;SelectListItem&gt; from an IEnumerable&lt;T&gt;.
    /// </summary>
    /// <typeparam name="T">The type of the elements of enumerable.</typeparam>
    /// <param name="enumerable">The IEnumerable&lt;T&gt; to create a IEnumerable&lt;SelectListItem&gt; from.</param>
    /// <param name="value">A function to extract a value from each element.</param>
    /// <param name="selectedValues">Items matching these values will be initially selected.</param>
    /// <returns>An IEnumerable&lt;SelectListItem&gt; that contains elements from the IEnumerable&lt;T&gt;.</returns>
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, IEnumerable<object> selectedValues)
    {
        return enumerable.ToSelectList(value, value, selectedValues);
    }

    /// <summary>
    /// Creates an IEnumerable&lt;SelectListItem&gt; from an IEnumerable&lt;T&gt;.
    /// </summary>
    /// <typeparam name="T">The type of the elements of enumerable.</typeparam>
    /// <param name="enumerable">The IEnumerable&lt;T&gt; to create a IEnumerable&lt;SelectListItem&gt; from.</param>
    /// <param name="value">A function to extract a value from each element.</param>
    /// <param name="text">A function to extract display text from each element.</param>
    /// <param name="selectAll">Whether all values are selected.</param>
    /// <returns>An IEnumerable&lt;SelectListItem&gt; that contains elements from the IEnumerable&lt;T&gt;.</returns>
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, bool selectAll = false)
    {
        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = selectAll
            };
        }
    }

    /// <summary>
    /// Creates an IEnumerable&lt;SelectListItem&gt; from an IEnumerable&lt;T&gt;.
    /// </summary>
    /// <typeparam name="T">The type of the elements of enumerable.</typeparam>
    /// <param name="enumerable">The IEnumerable&lt;T&gt; to create a IEnumerable&lt;SelectListItem&gt; from.</param>
    /// <param name="value">A function to extract a value from each element.</param>
    /// <param name="text">A function to extract display text from each element.</param>
    /// <param name="selectedValue">Items matching this value will be initially selected.</param>
    /// <returns>An IEnumerable&lt;SelectListItem&gt; that contains elements from the IEnumerable&lt;T&gt;.</returns>
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, object selectedValue)
    {
        return enumerable.ToSelectList(value, text, new List<object>() { selectedValue });
    }

    /// <summary>
    /// Creates an IEnumerable&lt;SelectListItem&gt; from an IEnumerable&lt;T&gt;.
    /// </summary>
    /// <typeparam name="T">The type of the elements of enumerable.</typeparam>
    /// <param name="enumerable">The IEnumerable&lt;T&gt; to create a IEnumerable&lt;SelectListItem&gt; from.</param>
    /// <param name="value">A function to extract a value from each element.</param>
    /// <param name="text">A function to extract display text from each element.</param>
    /// <param name="selectedValues">Items matching these values will be initially selected.</param>
    /// <returns>An IEnumerable&lt;SelectListItem&gt; that contains elements from the IEnumerable&lt;T&gt;.</returns>
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, IEnumerable<object> selectedValues)
    {
        var sel = selectedValues != null
            ? selectedValues.Where(x => x != null).ToList().ConvertAll<string>(x => x.ToString())
            : new List<string>();

        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = sel.Contains(value(f).ToString())
            };
        }
    }
}
Programming , , , ,