次の方法で共有


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

  • Anonymous
    November 10, 2008
    PingBack from http://www.tmao.info/checkboxlist-helper-for-mvc/

  • Anonymous
    November 13, 2008
    So I was thinking tonight, what if I want my MVC application to serve images that are stored in a SQL

  • Anonymous
    February 20, 2009
    Just what I was looking for - thanks!

  • Anonymous
    May 17, 2009
    The comment has been removed

  • Anonymous
    June 01, 2009
    Thanks for your code contribution. Is there a specific reason you created CheckBoxListInfo instead of reusing the existing SelectListItem class?

  • Anonymous
    June 01, 2009
    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.

  • Anonymous
    June 24, 2009
    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!

  • Anonymous
    July 01, 2009
    @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

  • Anonymous
    July 04, 2009
    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!

  • Anonymous
    July 04, 2009
    I forgot to mention that my implementation have support for ModelState, that's why I mentioned it :P

  • Anonymous
    July 09, 2009
    The comment has been removed

  • Anonymous
    September 07, 2009
    Nice example, works fine. Unfortunately this doesn't support ModelState and thus validation will be a bit harder.

  • Anonymous
    October 31, 2009
    Great solution for creating a checkboxlist. You just saved me numerous hours of work. Thank you!

  • Anonymous
    November 17, 2009
    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();        }    }

  • Anonymous
    January 07, 2010
    The comment has been removed

  • Anonymous
    March 29, 2010
    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

  • Anonymous
    March 25, 2012
    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

  • Anonymous
    April 11, 2012
    @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());