247

Caution: This question is over nine years old!

Your best option is to search for newer questions, or to search the answers below looking for your specific version of MVC, as many answers here are obsolete now.

If you do find an answer that works for your version, please make sure the answer contains the version of MVC you are using.
(The original question starts below)


This seems a bit bizarre to me, but as far as I can tell, this is how you do it.

I have a collection of objects, and I want users to select one or more of them. This says to me "form with checkboxes." My objects don't have any concept of "selected" (they're rudimentary POCO's formed by deserializing a wcf call). So, I do the following:

public class SampleObject{
  public Guid Id {get;set;}
  public string Name {get;set;}
}

In the view:

<%
    using (Html.BeginForm())
    {
%>
  <%foreach (var o in ViewData.Model) {%>
    <%=Html.CheckBox(o.Id)%>&nbsp;<%= o.Name %>
  <%}%>
  <input type="submit" value="Submit" />
<%}%>

And, in the controller, this is the only way I can see to figure out what objects the user checked:

public ActionResult ThisLooksWeird(FormCollection result)
{
  var winnars = from x in result.AllKeys
          where result[x] != "false"
          select x;
  // yadda
}

Its freaky in the first place, and secondly, for those items the user checked, the FormCollection lists its value as "true false" rather than just true.

Obviously, I'm missing something. I think this is built with the idea in mind that the objects in the collection that are acted upon within the html form are updated using UpdateModel() or through a ModelBinder.

But my objects aren't set up for this; does that mean that this is the only way? Is there another way to do it?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 2
    Others may find this solution useful: http://stackoverflow.com/questions/3291501/asp-net-mvc-maintain-state-of-a-dynamic-list-of-checkboxes/3298821#3298821 – Aaron Jul 21 '10 at 11:51

22 Answers22

262

Html.CheckBox is doing something weird - if you view source on the resulting page, you'll see there's an <input type="hidden" /> being generated alongside each checkbox, which explains the "true false" values you're seeing for each form element.

Try this, which definitely works on ASP.NET MVC Beta because I've just tried it.

Put this in the view instead of using Html.CheckBox():

<% using (Html.BeginForm("ShowData", "Home")) {  %>
  <% foreach (var o in ViewData.Model) { %>
    <input type="checkbox" name="selectedObjects" value="<%=o.Id%>">
    <%= o.Name %>
  <%}%>
  <input type="submit" value="Submit" />
<%}%>

Your checkboxes are all called selectedObjects, and the value of each checkbox is the GUID of the corresponding object.

Then post to the following controller action (or something similar that does something useful instead of Response.Write())

public ActionResult ShowData(Guid[] selectedObjects) {
    foreach (Guid guid in selectedObjects) {
        Response.Write(guid.ToString());
    }
    Response.End();
    return (new EmptyResult());
}

This example will just write the GUIDs of the boxes you checked; ASP.NET MVC maps the GUID values of the selected checkboxes into the Guid[] selectedObjects parameter for you, and even parses the strings from the Request.Form collection into instantied GUID objects, which I think is rather nice.

Dylan Beattie
  • 53,688
  • 35
  • 128
  • 197
  • Yup! This is what I have had to do for my applications too! – Adhip Gupta Oct 21 '08 at 00:14
  • 70
    wtf. hidden input fields with the SAME name as the control. its ViewState 2.0 ! – Simon_Weaver Jan 26 '09 at 09:20
  • 3
    This is also present in the 1.0 release. Look at the answer @andrea-balducci submitted that gives you an intelligent way to deal with this. If the checkbox is not checked, the resulting text retrieved should be 'false false' - its a good workaround to account for brain dead browsers... – Astra Mar 27 '09 at 20:03
  • 77
    Surprise? Wtf? The hidden input has the same name as the checkbox - if the checkbox by the same name isn't checked then its value isn't posted whereas the value of the hidden is posted. The first time the browser encounters a named element it will use that value and ignore all other elements with the same name. This guarantees that a value is submitted: true if the checkbox is checked (assuming it's found above the hidden element) and false if checkbox is unchecked (the empty checkbox is ignored and the hidden becomes the fall back). The real wtf is why nobody else pointed this out. – nerraga Aug 17 '09 at 20:33
  • 19
    @nerraga: You're wrong: it's valid html to have multiple form elements with the same name; and if so, *all* elements are posted, and I'm not sure the order is actually defined (though likely to be simply in page order in practice). Using the word "false" as the value is somewhat misleading, so yeah, it's a WTF - a better, less misleading choice would have been something like "exists" or some meaningless token like a dash. – Eamon Nerbonne Dec 08 '10 at 17:27
  • @nerrega wtf! I did! on January 26th 2009! see below - or above - or here http://stackoverflow.com/a/479220/16940 – Simon_Weaver Sep 21 '12 at 02:31
95

HtmlHelper adds an hidden input to notify the controller about Unchecked status. So to have the correct checked status:

bool bChecked = form[key].Contains("true");
Andrea Balducci
  • 2,834
  • 1
  • 21
  • 18
  • 1
    This is the easiest approach to this problem and it's what I always use. It allows you to use the helper methods and account for ASP.NET MVCs behavior. – Nick Daniels Nov 19 '10 at 14:41
  • 8
    .. or in the case for not checked: bool bChecked = form[key].Equals("false"); Doing a .Contains("false") fails as the true value is "true,false". – Mark Robinson Jan 24 '11 at 11:25
54

In case you're wondering WHY they put a hidden field in with the same name as the checkbox the reason is as follows :

Comment from the sourcecode MVCBetaSource\MVC\src\MvcFutures\Mvc\ButtonsAndLinkExtensions.cs

Render an additional <input type="hidden".../> for checkboxes. This addresses scenarios where unchecked checkboxes are not sent in the request. Sending a hidden input makes it possible to know that the checkbox was present on the page when the request was submitted.

I guess behind the scenes they need to know this for binding to parameters on the controller action methods. You could then have a tri-state boolean I suppose (bound to a nullable bool parameter). I've not tried it but I'm hoping thats what they did.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 4
    Yeah this is handy in scenarios where you have paged grids etc and you want to unselect at item that was previously selected in some business object. – RichardOD Jul 31 '09 at 14:45
49

You should also use <label for="checkbox1">Checkbox 1</label> because then people can click on the label text as well as the checkbox itself. Its also easier to style and at least in IE it will be highlighted when you tab through the page's controls.

<%= Html.CheckBox("cbNewColors", true) %><label for="cbNewColors">New colors</label>

This is not just a 'oh I could do it' thing. Its a significant user experience enhancement. Even if not all users know they can click on the label many will.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
27

I'm surprised none of these answers used the built in MVC features for this.

I wrote a blog post about this here, which even actually links the labels to the checkbox. I used the EditorTemplate folder to accomplish this in a clean and modular way.

You will simply end up with a new file in the EditorTemplate folder that looks like this:

@model SampleObject

@Html.CheckBoxFor(m => m.IsChecked)
@Html.HiddenFor(m => m.Id)
@Html.LabelFor(m => m.IsChecked, Model.Id)

in your actual view, there will be no need to loop this, simply 1 line of code:

@Html.EditorFor(x => ViewData.Model)

Visit my blog post for more details.

Shawn Mclean
  • 56,733
  • 95
  • 279
  • 406
  • 1
    Thank you! Everboyd still using the old forms ways. And when you submit you can access the Model without all this collection[] and request.form garbage - This is what I was looking for. `+1` and +beer – Piotr Kula Mar 11 '13 at 14:11
  • 2
    This should be the top answer for the question. Very simple and easy to implement. – Johan G Nov 12 '14 at 18:20
  • Struggled for some time to find an elegant solution to this before I found this answer. It's a few year's old but still perfectly valid in 2016! If using in several instances, use the built-in SelectListItem as a generic type which is perfectly suited to this – Phil Jan 05 '16 at 17:17
  • Is SampleObject a list? Such as: @ModelType List(Of Type) – JoshYates1980 Jun 10 '16 at 20:52
25

Here's what I've been doing.

View:


<input type="checkbox" name="applyChanges" />

Controller:


var checkBox = Request.Form["applyChanges"];

if (checkBox == "on")
{
...
}

I found the Html.* helper methods not so useful in some cases, and that I was better off doing it in plain old HTML. This being one of them, the other one that comes to mind is radio buttons.

Edit: this is on Preview 5, obviously YMMV between versions.

mmacaulay
  • 3,049
  • 23
  • 27
10

They appear to be opting to read the first value only, so this is "true" when the checkbox is checked, and "false" when only the hidden value is included. This is easily fetched with code like this:

model.Property = collection["ElementId"].ToLower().StartsWith("true");
Fluffy
  • 101
  • 1
  • 2
8

@Dylan Beattie Great Find!!! I Thank you much. To expand even further, this technique also works perfect with the View Model approach. MVC is so cool, it's smart enough to bind an array of Guids to a property by the same name of the Model object bound to the View. Example:

ViewModel:

public class SampleViewModel
{
    public IList<SampleObject> SampleObjectList { get; set; }
    public Guid[] SelectedObjectIds { get; set; }

    public class SampleObject
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
}

View:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Sample View</h2>
<table>
    <thead> 
        <tr>
            <th>Checked</th>
            <th>Object Name</th>
        </tr>
    </thead> 
<% using (Html.BeginForm()) %>
<%{%>                    
    <tbody>
        <% foreach (var item in Model.SampleObjectList)
           { %>
            <tr>
                <td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
                <td><%= Html.Encode(item.Name)%></td>
            </tr>
        <% } %>
    </tbody>
</table>
<input type="submit" value="Submit" />
<%}%>                    

Controller:

    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult SampleView(Guid id)
    {
        //Object to pass any input objects to the View Model Builder 
        BuilderIO viewModelBuilderInput = new BuilderIO();

        //The View Model Builder is a conglomerate of repositories and methods used to Construct a View Model out of Business Objects
        SampleViewModel viewModel = sampleViewModelBuilder.Build(viewModelBuilderInput);

        return View("SampleView", viewModel);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SampleView(SampleViewModel viewModel)
    {
        // The array of Guids successfully bound to the SelectedObjectIds property of the View Model!
        return View();
    }

Anyone familiar with the View Model philosophy will rejoice, this works like a Champ!

nautic20
  • 81
  • 1
  • 1
6

I'd also like to point out that you can name each checkbox a different name, and have that name part of the actionresults parameters.

Example,

View:

 <%= Html.CheckBox("Rs232CheckBox", false, new { @id = "rs232" })%>RS-232

 <%= Html.CheckBox("Rs422CheckBox", false, new { @id = "rs422" })%>RS-422

Controller:

public ActionResults MyAction(bool Rs232CheckBox, bool Rs422CheckBox) {
    ...
}

The values from the view are passed to the action since the names are the same.

I know this solution isn't ideal for your project, but I thought I'd throw the idea out there.

Darcy
  • 5,228
  • 12
  • 53
  • 79
5
<input type = "checkbox" name = "checkbox1" /> <label> Check to say hi.</label>

From the Controller:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Index(FormCollection fc)
    {

         var s = fc["checkbox1"];

         if (s == "on")
         {
             string x = "Hi";
         }
    }
bluwater2001
  • 7,829
  • 5
  • 24
  • 21
4

From what I can gather, the model doesn't want to guess whether checked = true or false, I got around this by setting a value attribute on the checkbox element with jQuery before submitting the form like this:

 $('input[type="checkbox"]').each(function () {
       $(this).attr('value', $(this).is(':checked'));
 }); 

This way, you don't need a hidden element just to store the value of the checkbox.

BraveNewMath
  • 8,090
  • 5
  • 46
  • 51
4

I know that this question was written when MVC3 wasn't out, but for anyone who comes to this question and are using MVC3, you may want the "correct" way to do this.

While I think that doing the whole

Contains("true");

thing is great and clean, and works on all MVC versions, the problem is that it doesn't take culture into account (as if it really matters in the case of a bool).

The "correct" way to figure out the value of a bool, at least in MVC3, is to use the ValueProvider.

var value = (bool)ValueProvider.GetValue("key").ConvertTo(typeof(bool));

I do this in one of my client's sites when I edit permissions:

var allPermissionsBase = Request.Params.AllKeys.Where(x => x.Contains("permission_")).ToList();
var allPermissions = new List<KeyValuePair<int, bool>>();

foreach (var key in allPermissionsBase)
{
     // Try to parse the key as int
     int keyAsInt;
     int.TryParse(key.Replace("permission_", ""), out keyAsInt);

     // Try to get the value as bool
     var value = (bool)ValueProvider.GetValue(key).ConvertTo(typeof(bool));
}

Now, the beauty of this is you can use this with just about any simple type, and it will even be correct based on the Culture (think money, decimals, etc).

The ValueProvider is what is used when you form your Actions like this:

public ActionResult UpdatePermissions(bool permission_1, bool permission_2)

but when you are trying to dynamically build these lists and check the values, you will never know the Id at compile time, so you have to process them on the fly.

Dan VanWinkle
  • 991
  • 11
  • 20
  • is there a reason you use Request.Params instead of adding a FormCollection parameter to the method? – SwissCoder Oct 23 '12 at 09:55
  • No reason, I don't really see any benefit to one way over the other. – Dan VanWinkle Oct 24 '12 at 06:52
  • 1
    Sorry, there is actually a reason. Just looked at my code, and I am using this one method for 5 different calls, and I didn't feel like passing FormCollection from each method. This was the easiest way to get the keys without needing to pass anything around. – Dan VanWinkle Oct 24 '12 at 07:07
  • yeah I am new to MVC. And I read somewhere that it's better in general to use the FormCollection instead of Request.Params, same as using a typed Model instead of the FormCollection (so actually it would be best to use a model and avoid using Request.Params in general). I noticed other things of the code are not used, like the allPermissions variable and the keyAsInt. Thanks for answering. – SwissCoder Oct 24 '12 at 08:05
4

This issue is happening in the release 1.0 as well. Html.Checkbox() causes another hidden field to be added with the same name/id as of your original checkbox. And as I was trying loading up a checkbox array using document.GetElemtentsByName(), you can guess how things were getting messed up. It's a bizarre.

Farhan Zia
  • 283
  • 1
  • 2
  • 9
3

Same as nautic20's answer, just simply use MVC default model binding checkbox list with same name as a collection property of string/int/enum in ViewModel. That is it.

But one issue need to point out. In each checkbox component, you should not put "Id" in it which will affect MVC model binding.

Following code will work for model binding:

 <% foreach (var item in Model.SampleObjectList)
       { %>
        <tr>
            <td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
            <td><%= Html.Encode(item.Name)%></td>
        </tr>
 <% } %>

Following codes will not binding to model (difference here is it assigned id for each checkbox)

<% foreach (var item in Model.SampleObjectList)
       { %>
        <tr>
            <td><input type="checkbox" name="SelectedObjectIds" id="[some unique key]" value="<%= item.Id%>" /></td>
            <td><%= Html.Encode(item.Name)%></td>
        </tr>
<% } %>
ChinaHelloWorld
  • 1,007
  • 1
  • 12
  • 6
  • Thanks for answering, but this question is *juuuust* a bit old. Six years old, about. You might want to [edit] and specify which version of MVC you're using. That will help anybody using a newer version locate this answer. –  Mar 11 '14 at 17:41
  • I second that. Removing the unique ID solved this issue after much headaches and gnashing of teeth – Ody Apr 22 '15 at 09:22
  • The version of MVC i am using is .net MVC 4.0 for this issue. – ChinaHelloWorld Jun 10 '15 at 15:05
3

The easiest way to do is so...

You set the name and value.

<input type="checkbox" name="selectedProducts" value="@item.ProductId" />@item.Name

Then on submitting grab the values of checkboxes and save in an int array. then the appropriate LinQ Function. That's it..

[HttpPost]
        public ActionResult Checkbox(int[] selectedObjects)
        {
            var selected = from x in selectedObjects
                           from y in db
                           where y.ObjectId == x
                           select y;                   

            return View(selected);
        }
kk-dev11
  • 2,654
  • 5
  • 37
  • 48
2

this is what i did to loose the double values when using the Html.CheckBox(...

Replace("true,false","true").Split(',')

with 4 boxes checked, unchecked, unchecked, checked it turns true,false,false,false,true,false into true,false,false,true. just what i needed

Jeroen
  • 21
  • 1
0

I had nearly the same Problem but the return Value of my Controller was blocked with other Values.

Found a simple Solution but it seems a bit rough.

Try to type Viewbag. in your Controller and now you give it a name like Viewbag.Checkbool

Now switch to the View and try this @Viewbag.Checkbool with this you will get the value out of the Controller.

My Controller Parameters look like this:

public ActionResult Anzeigen(int productid = 90, bool islive = true)

and my Checkbox will update like this:

<input id="isLive" type="checkbox" checked="@ViewBag.Value" ONCLICK="window.location.href = '/MixCategory/Anzeigen?isLive=' + isLive.checked.toString()" />
treborian
  • 35
  • 3
0

Using @mmacaulay , I came up with this for bool:

// MVC Work around for checkboxes.
bool active = (Request.Form["active"] == "on");

If checked active = true

If unchecked active = false

Ravi Ram
  • 24,078
  • 21
  • 82
  • 113
0

How about something like this?

bool isChecked = false;
if (Boolean.TryParse(Request.Form.GetValues(”chkHuman”)[0], out isChecked) == false)
    ModelState.AddModelError(”chkHuman”, “Nice try.”);
Skadoosh
  • 2,575
  • 8
  • 40
  • 53
0

My solution is:

<input type="checkbox"  id="IsNew-checkbox"  checked="checked" />     
<input type="hidden"  id="IsNew" name="IsNew" value="true"  />      
<script language="javascript" type="text/javascript" >     
  $('#IsNew-checkbox').click(function () {    
      if ($('#IsNew-checkbox').is(':checked')) {    
          $('#IsNew').val('true');    
      } else {    
          $('#IsNew').val('false');    
       }    
  });   
</script>  

More you can find here: http://www.blog.mieten.pl/2010/12/asp-net-mvc-custom-checkbox-as-solution-of-string-was-not-recognized-as-a-valid-boolean/

bensiu
  • 24,660
  • 56
  • 77
  • 117
pawlom84
  • 9
  • 1
  • You don't need to use any Javascript for this ... var isChecked = formData[key].Contains("true"); – Jammer Apr 16 '13 at 08:36
0

When using the checkbox HtmlHelper, I much prefer to work with the posted checkbox form data as an array. I don't really know why, I know the other methods work, but I think I just prefer to treat comma separated strings as an array as much as possible.

So doing a 'checked' or true test would be:

//looking for [true],[false]
bool isChecked = form.GetValues(key).Contains("true"); 

Doing a false check would be:

//looking for [false],[false] or [false]
bool isNotChecked = !form.GetValues(key).Contains("true"); 

The main difference is to use GetValues as this returns as an array.

eyesnz
  • 2,698
  • 2
  • 16
  • 27
0

Just do this on $(document).ready :

$('input:hidden').each(function(el) {
    var that = $(this)[0];
    if(that.id.length < 1 ) {

        console.log(that.id);
        that.parentElement.removeChild(that);

    }
});
Sparkup
  • 3,686
  • 2
  • 36
  • 50
doronAv
  • 1
  • 1
  • 2
    Unfortunately, you might get strange results on the server side for people with JavaScript turned off (NoScript plug-in, old mobile phones, Lynx...) – ArIck Sep 15 '11 at 21:55