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.

Comments (18)
  1. So I was thinking tonight, what if I want my MVC application to serve images that are stored in a SQL

  2. James says:

    Just what I was looking for – thanks!

  3. Not being a strong C# coder I was grateful to find a re-implementation of the Html.CheckBoxList helper that was dropped in MVC Preview 5. I’m beginning to realise how much better my C# is going to have to get to be able to get the best out of ASP.NET MVC. However, after more than 10 years my raw HTML is pretty good 😉 and in return for you generously sharing your code though I should point out that the HTML generated is invalid.

    It’s probably the use of TagRenderMode.Normal but INPUT elements are defined as EMPTY and closing tags are FORBIDDEN. Check the W3C specs at http://www.w3.org/TR/html401/interact/forms.html#edef-INPUT. Also, it’s best to associate the text as a LABEL element with the INPUT for accessibility and semantics. For example (hope the HTML makes it via this comment):

    <input type="checkbox" id="red" value="red" name="colour" />

    <label for="red">Red</label>

    To achieve this I modified the TagBuilder section as follows:

    TagBuilder inputBuilder = new TagBuilder("input");

    if (info.IsChecked) inputBuilder.MergeAttribute("checked", "checked");

    inputBuilder.MergeAttributes<string, object>(htmlAttributes);

    inputBuilder.MergeAttribute("type", "checkbox");

    inputBuilder.MergeAttribute("value", info.Value);

    inputBuilder.MergeAttribute("name", name);

    inputBuilder.GenerateId(info.Value);

    TagBuilder labelBuilder = new TagBuilder("label");

    labelBuilder.MergeAttribute("for", inputBuilder.Attributes["id"]);

    labelBuilder.InnerHtml = info.DisplayText;

                   sb.Append(inputBuilder.ToString(TagRenderMode.SelfClosing));

    sb.Append(labelBuilder.ToString(TagRenderMode.Normal));

    sb.Append("<br />");

  4. John Kaster says:

    Thanks for your code contribution. Is there a specific reason you created CheckBoxListInfo instead of reusing the existing SelectListItem class?

  5. John Kaster says:

    Also, you said: "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."

    I think you could use FormCollection formValues for this scenario.

  6. Hi Jeremiah!

    I’m using this helper with the final version of ASP.NET MVC.

    The problem is the save method…  When I receive a FormCollection, it brings all the checkboxes values.. but only the value.. and I don’t know if it’s checked or not.

    When I receive a string [] it brings me nothing…

    Do you know how I can do the save method?

    Thanks!

  7. Tim Rourke says:

    @Andre Nascentes:

    You might try this:

    bool bChecked = form[key].Contains("true");

    I found it on StackOverflow at http://stackoverflow.com/questions/220020/how-to-handle-checkboxes-in-asp-net-mvc-forms/220073

  8. Nice implementation, but unfortunately, it doesn’t have support for ModelState.

    I made a similar custom implementation of CheckListBox based on the CheckBox() extension method from ASP.NET MVC.

    If you are interested in it you may find it here:

    http://gerardocontijoch.wordpress.com/2009/07/04/un-checkboxlist-que-funciona-en-asp-net-mvc/

    The post is in spanish, but I guess nobody will have problem reading the code.

    The only limitation I find in my implementation is that the checkstate of the checkboxes can only be loaded from the ModelState and you can’t set it by hand.

    Great blog, btw!

  9. I forgot to mention that my implementation have support for ModelState, that’s why I mentioned it 😛

  10. vp says:

    I have used your code. but I am getting the below error  "best extension method overload ——-" has some invalid arguments…

    i have used below code

    <div><%=Html.CheckBoxList("test",ViewData.Model)%></div>

    ViewData.Model – here i have passed lsit..

    still am getting invalid argumets error

  11. Lauri Larjo says:

    Nice example, works fine. Unfortunately this doesn’t support ModelState and thus validation will be a bit harder.

  12. jmuir says:

    Great solution for creating a checkboxlist. You just saved me numerous hours of work. Thank you!

  13. Chris says:

    Thanks for sharing this. I need my list broken into table columns so I extended this a little further and thought I would share it.

    Passing in a new variable I call ‘split’ that represents the number of columns I want in the table. Code then evenly splits them into table columns which look nice 🙂

    Passing <= 1 or not passing a number makes it work as normal:

    Here is the code:

     public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> listInfo)

           {

               return htmlHelper.CheckBoxList(name, listInfo, 0,

                   ((IDictionary<string, object>)null));

           }

           public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> listInfo, int split)

           {

               return htmlHelper.CheckBoxList(name, listInfo, split,

                   ((IDictionary<string, object>)null));

           }

           public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> listInfo,

              object htmlAttributes)

           {

               return htmlHelper.CheckBoxList(name, listInfo, 0,

                   ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));

           }

           public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> listInfo, int split,

               IDictionary<string, object> htmlAttributes)

           {

               if (String.IsNullOrEmpty(name))

                   throw new ArgumentException("The argument must have a value", "name");

               if (listInfo == null)

                   throw new ArgumentNullException("listInfo");

               if (listInfo.Count < 1)

                   throw new ArgumentException("The list must contain at least one value", "listInfo");

               StringBuilder sb = new StringBuilder();

               int colCount = 0;

               int count = 0;

               int maxItems = listInfo.Count();

               if (split > 1)

               {

                   // Begin Table

                   sb.AppendLine("<table width=’100%’ border=’0′><tr><td>");

               }

               foreach (CheckBoxListInfo info in listInfo)

               {

                   TagBuilder builder = new TagBuilder("input");

                   if (info.IsChecked) builder.MergeAttribute("checked", "checked");

                   builder.MergeAttributes<string, object>(htmlAttributes);

                   builder.MergeAttribute("type", "checkbox");

                   builder.MergeAttribute("value", info.Value);

                   builder.MergeAttribute("name", name);

                   builder.InnerHtml = info.DisplayText;

                   sb.Append(builder.ToString(TagRenderMode.Normal));

                   if (split <= 1)

                   {

                       // Skip Table all together….

                   }

                   else

                   {

                       count ++;

                       colCount ++;

                       if (split == colCount)

                       {

                           colCount = 0;

                           sb.Append("</td></tr>");

                           if (count != maxItems)

                           {

                               // Need another row

                               sb.Append("<tr><td>");

                           }

                       }

                       else

                       {

                           sb.Append("</td><td>");

                       }

                   }

               }

               if (split > 1)

                   sb.Append("</table>");

               return sb.ToString();

           }

       }

  14. Ritz says:

    have used below code

    <div><%=Html.CheckBoxList("test",ViewData.Model)%></div>

    ViewData.Model – here i have passed lsit..

    still am getting invalid argumets error

  15. Kirti says:

    Hi

    this is Great helper class but one problem when

    we not select any checkbox from checkboxlist that time key is not find

    in collection

    collection

  16. suma says:

    Hi, i have implemented this code in my solution but with few issues.

    Instead of displaying the controls (checkbox controls) the view is displaying me input tags like

    <input name="chklist" type="checkbox" value="1">Yes</input><br /><input name="chklist" type="checkbox" value="2">No</input><br />

    Please help

  17. Borshlite says:

    @suma

    I was having the same problem, and solved it by having the CheckBoxList methods return a MvcHtmlString instead of string.  To do this, you'll have to return the stringbulder as return MvcHtmlString.Create(sb.ToString());

Comments are closed.

Skip to main content