CheckBoxList Helper for MVC

The early previews of the MVC Toolkit contained a few helpers that are not available in the current MVC Beta and MVC Beta Futures.  On of the ones that was nixed was the CheckBoxList helper.  I was in need of this type of functionality lately and found myself out of luck.  I needed to add a dynamic list of checkboxes to a form, like the roles that a user could possible be a member of.  These roles could be added to or deleted from at any time.

I looked to the Html.CheckBox helper to see if that would work.  This helper can be used like so:

    1: /* The model for this is a Dictionary<string,bool> that contains 
    2:    the name for the role and whether the user is a member */
    3:  
    4: <% foreach (var info in ViewData.Model) { %>
    5:     <div><%= Html.CheckBox(info.Key, info.Value) %> <%= info.Key %></div>
    6: <% } %>

The problem with using Html.CheckBox for my scenario arises when you need to get the values in the form ActionMethod.  For Html.CheckBox, the form handler is expecting a boolean parameter for each checkbox.  An example is show below.

    1: [AcceptVerbs(HttpVerbs.Post)]
    2: public ActionResult Roles(bool administrator, bool user, bool poweruser)
    3: {
    4:     // Each bool parameter is the checked value for the checkbox that 
    5:     // has the same name.  If my list of roles is dynamic, this does
    6:     // not work so well :(
    7: }

As you can see, this would not satisfy the requirements that I had.  So what is the answer?  Create my own CheckBoxList helper of course.

Here is the extension method code for my implementation, along with a simple class that contains the info needed for each checkbox in the list..

    1: public static class InputExtensions
    2: {
    3:     public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> listInfo)
    4:     {
    5:         return htmlHelper.CheckBoxList(name, listInfo,
    6:             ((IDictionary<string, object>) null));
    7:     }
    8:  
    9:     public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> listInfo,
   10:         object htmlAttributes)
   11:     {
   12:         return htmlHelper.CheckBoxList(name, listInfo, 
   13:             ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
   14:     }
   15:  
   16:     public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> listInfo,
   17:         IDictionary<string, object> htmlAttributes)
   18:     {
   19:         if (String.IsNullOrEmpty(name))
   20:             throw new ArgumentException("The argument must have a value", "name");
   21:         if (listInfo == null)
   22:             throw new ArgumentNullException("listInfo");
   23:         if (listInfo.Count < 1)
   24:             throw new ArgumentException("The list must contain at least one value", "listInfo");
   25:  
   26:         StringBuilder sb = new StringBuilder();
   27:  
   28:         foreach (CheckBoxListInfo info in listInfo)
   29:         {
   30:             TagBuilder builder = new TagBuilder("input");
   31:             if (info.IsChecked) builder.MergeAttribute("checked", "checked");
   32:             builder.MergeAttributes<string, object>(htmlAttributes);
   33:             builder.MergeAttribute("type", "checkbox");
   34:             builder.MergeAttribute("value", info.Value);
   35:             builder.MergeAttribute("name", name);
   36:             builder.InnerHtml = info.DisplayText;
   37:             sb.Append(builder.ToString(TagRenderMode.Normal));
   38:             sb.Append("<br />");
   39:         }
   40:  
   41:         return sb.ToString();
   42:     }
   43: }
   44:  
   45: // This the information that is needed by each checkbox in the
   46: // CheckBoxList helper.
   47: public class CheckBoxListInfo
   48: {
   49:     public CheckBoxListInfo(string value, string displayText, bool isChecked)
   50:     {
   51:         this.Value = value;
   52:         this.DisplayText = displayText;
   53:         this.IsChecked = isChecked;
   54:     }
   55:  
   56:     public string Value { get; private set; }
   57:     public string DisplayText { get; private set; }
   58:     public bool IsChecked { get; private set; }
   59: }

This can then be used to render the list of checkboxes in a View as shown below:

    1: /* Where ViewData.Model is a List of CheckBoxListInfo objects that
    2:    provide the details for the checkboxes. */
    3:  
    4: <div><%= Html.CheckBoxList("roles", ViewData.Model) %></div>

And now, in the post ActionMethod, we can access the ones that have been checked like so:

    1: [AcceptVerbs(HttpVerbs.Post)]
    2: public ActionResult Roles(string[] roles)
    3: {
    4:     /* The 'roles' parameter contains the values from the 
    5:        checkboxes that were checked. */
    6: }

In addition to solving the issue that I was having, this post shows how easy it is to implement your own custom helpers in MVC.