ASP MVC2 typesafe generic listbox renderer

I was putting together my first listbox in asp.net mvc2, and found that it would be quite tedious doing this a lot. Beeing a programmer, and thus lazy, I started thinking about a solution for this. Here’s what I came up with.

These are the steps you need to take for building a listbox:

//a list of objects containing the date you want to put in the listbox
List<Model> data = new List<Model>() {
    new Model() { ID = 1, Name = "Model1" },
    new Model() { ID = 2, Name = "Model2" }
};

//create a list of SelectListItem objects
List<SelectListItem> items = new List<SelectListItem>();
foreach (Model m in data) {
    items.Add(new SelectListItem() { Text = m.Name, Value = m.ID });
}

//pass to view
ViewData["selectList"] = items;

//and this code coes in the view
<%= Html.ListBoxFor(model => model.Users, ViewData["selectList"] as List<SelectListItem>) %>

This means that everytime I need to create a listbox I need to manually convert the data object into SelectListItems. This is common functionallity and can also easily be abstracted. I created a new class called HtmlHelper and created some static methods to help convert object for usage in a listbox.

    public class HtmlHelper
    {
        #region SelectList

        public static List<SelectListItem> CreateSelectListItems<T>(List<T> objects, Func<T, string> value, Func<T, string> text)
        {
            return CreateSelectListItems<T>(objects, value, text, null);
        }

        public static List<SelectListItem> CreateSelectListItems<T>(List<T> objects, Func<T, string> value, Func<T, string> text, string selected)
        {
            return CreateSelectListItems<T>(objects, value, text, null, m => true);
        }

        /**
         * This excepts the following parameters:
         * - A list of objects containing the data
         * - a lambda to abstract the data for the value field
         * - a labmda to abstract the text for the text field
         * - a string indicating which value must be selected
         * - a filter to select only specific data objects to abstract the info from
         */
        public static List<SelectListItem> CreateSelectListItems<T>(List<T> objects, Func<T, string> value, Func<T, string> text, string selected, Func<T, bool> filter) 
        {
            //filter out the wanted objects and extract the wanted data info a SelectListItem
            List<SelectListItem> result = (from o in objects
                                           where filter(o) 
                                           select new SelectListItem() { Value = value(o), Text = text(o) }).ToList();
            //Select an item if wanted and possible
            if (string.IsNullOrEmpty(selected)) {
                SelectListItem select = result.Find(i => i.Value == selected);
                if (null != select) select.Selected = true;
            }
            return result;
        }

        #endregion
    }

This is a typesafe solution, which fixes the problem easily. It abstracts the object creation away from our controller, and this class can also be unit tested very easy.
Using the same datalist as in the first code example, the rest of the controller code now looks better, and is generic.

//a list of objects containing the date you want to put in the listbox
List<Model> data = new List<Model>() {
    new Model() { ID = 1, Name = "Model1" },
    new Model() { ID = 2, Name = "Model2" }
};

//This is the end result, everything reduced to 1 line
ViewData["selectList"] = HtmlHelper.CreateSelectListItems<Model>(data, m => m.ID, m => m.Name);

//and this code coes in the view
<%= Html.ListBoxFor(model => model.Users, ViewData["selectList"] as List<SelectListItem>) %>

Feeling much better creating listboxes now 🙂