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 SQLAnonymous
February 20, 2009
Just what I was looking for - thanks!Anonymous
May 17, 2009
The comment has been removedAnonymous
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/220073Anonymous
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 :PAnonymous
July 09, 2009
The comment has been removedAnonymous
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 removedAnonymous
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 collectionAnonymous
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 helpAnonymous
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());