Basically I have a form that I am dynamically adding objects to. I am doing this with AJAX so can just initialise the object and return it with JSON. Each new object has a unique GUID assigned to it so we can identify each object in the model collection when it is passed back into the action.
However, I need to support non JavaScript so am trying to write a solution that will post back the model and add or remove the given object from the model. There can be any number of these new objects on the model so I need to pass back several things to find out which object to delete before returning the model back to the view. This could be either
a) The GUID for the object the user has deleted.
b) The button that has been clicked to identify which object to delete.
The problem is that the partial view is generic and I would like to keep it that way so I'm trying to pass the identifying GUID back with the input button on each partial view but don't know how. I can easily do this with JavaScript because I just remove the created AJAX object from the page before posting it when the user clicks the remove link but can't figure out how to do it with a submit. Basically I want to do something like this:
#using (Project.Namespace.Infrastructure.Helpers.HtmlPrefixScopeExtensions.HtmlFieldPrefixScope _scope = Html.BeginCollectionItem())
{
<ul class="ulMedicationsControl">
#Html.ActionLink("Remove This Object", "RemoveObject", null)
#Html.Input("RemoveObject", "Remove This Object", new { Prefix = _scope.Prefix, objectGUID = IdentifyingGUID })
#Html.HiddenFor(m => m.IdentifyingGUID);
<li class="liQuestion">
#Html.MandatoryLabelFor(m => m.myField)
#Html.TextBoxFor(m => m.myField)
</li>
</ul>
<div id="#(_scope.Prefix).ajaxPlaceholder"></div>
}
In the controller:
[ActionName("FormName")]
[AcceptParameter(Name = "RemoveObject", Value = "Remove This Object")]
public ActionResult RemoveObject(MyParentModel model, string Prefix, string objectGUID)
{
Guid ID = new Guid(objectGUID);
foreach (ObjectModel object in model.objects){
if (object.IdentifyingGUID == ID)
{
model.objects.Remove(object);
break;
}
}
return View(model);
}
Any help I would really appreciate as I simple can't figure out how to do this!
EDIT
Also just to add the prefix attribute simply identifies where in the form the object sits. This will be needed for me to find which object list to go through and remove the object from as there may be several lists in different placed in the model.
An HTML input only passes "name=value" when a form post occurs so that's all you have to work with. With <input type=submit> you're further limited by the fact that the button's value is its caption (i.e. "myControl=Click Me!" is posted), so you can't stick anything programmatically meaningful in the value.
Method 1: So you're left with encoding all the information you need into the input's name - an approach that works fine, but you'll have to have to go digging into the controller action method's FormCollection parameter rather than relying on model binding. For example:
<input name="delete$#(_scope.Prefix)$#objectGUID" type="submit" value="Delete me" />
Better, have a helper class that encapsulates the string format with a ToString override and has Parse/TryParse/etc static methods, which could be used like this:
<input name="#(new DeleteToken{Prefix=_scope.Prefix, objectGUID=IdentifyingGUID})" type="submit" value="Delete me" />
In your action method:
[HttpPost]
public ActionResult Foo(FormCollection formData)
{
var deleteTokens = DeleteToken.ParseAll(formData.AllKeys);
foreach (var token in deleteTokens)
{
//...do the deletion
}
}
Method 2: An alternative approach is to group each item into its own <form> (bear in mind you can't nest forms) - so when the submit happens, only its surrounding form is posted in which you can stash hidden inputs with the necessary data. e.g.
<ul class="ulMedicationsControl">
<form ... >
<!-- hidden field and submit button and whatever else here -->
...
</form>
</ul>
I have a multi-step file import process. I have a hidden form input in my view that I am trying to populate with the "CurrentStep" from the view model.
<% = Html.HiddenFor(model => model.CurrentStep) %>
CurrentStep is an Enum and I always get the default value rather than the one I provided to the view model. on the other hand this gets me the correct value:
<p><% = Model.CurrentStep %></p>
I realise I could just hand code the hidden input but I want to know: what am I doing wrong? Is there a better way to keep track of the current step between POSTs?
What you are doing wrong is that you are trying to modify the value of a POSTed variable in your controller action. So I suppose you are trying to do this:
[HttpPost]
public ActionResult Foo(SomeModel model)
{
model.CurrentStep = Steps.SomeNewValue;
return View(model);
}
and html helpers such as HiddenFor will always first use the POSTed value and after that the value in the model.
So you have a couple of possibilities:
Remove the value from the modelstate:
[HttpPost]
public ActionResult Foo(SomeModel model)
{
ModelState.Remove("CurrentStep");
model.CurrentStep = Steps.SomeNewValue;
return View(model);
}
Manually generate the hidden field
<input type="hidden" name="NextStep" value="<%= Model.CurrentStep %>" />
Write a custom helper which will use the value of your model and not the one that's being POSTed
My solution was to use Darin's second option, because option 1 (clearing from the model state) means hard coding a string (and the naming convention can be tricky with complex models), and wanted to avoid option 3 because I already have so many custom helpers.
<input type="hidden" name="#Html.NameFor(x => Model.SomeId)" value="#Model.SomeId" />
Just a reminder that you can use Html.NameFor to keep things clean.
Make sure you model property has a "set" operator.
This won't get updated on post-back:
#Html.HiddenFor( m => m.NoSeq)
public Class MyModel
{
int _NoSeq;
public NoSeq
{
get { return _NoSeq };
}
}
I haver a simple radio button list on my page that I render with the following in my view:
<label for="gender">Gender</label>
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.ValidationMessage("gender") %>
Note that when the user initially sees this input, neither button is selected.
The validation is there to force them to choose and not accept a default.
Therefore, these two radio buttons are bound to a nullable int property in my model declared as:
public int? gender { get; set; }
So if they do not select a button, and submit the page, the gender property will be null indicating that they did not select. The following validation is called by the controller during the post:
if (!gender.HasValue)
ModelState.AddModelError("gender", "gender required");
But, if the validation fails (they did not choose), then during the rendering phase, the following exception is thrown by the MVC framework:
System.NullReferenceException was unhandled by user code
Message="Object reference not set to an instance of an object."
In searching for for a solution to this problem, I noted several had this problem.
I am using ASP.NET MVC 1.0. I found the place in the code where this error is thrown using .NET Reflector.
The question is how to make this work correctly?
EDIT: to add stacktrace:
System.NullReferenceException was unhandled by user code
Message="Object reference not set to an instance of an object."
Source="System.Web.Mvc"
StackTrace:
at System.Web.Mvc.HtmlHelper.GetModelStateValue(String key, Type destinationType)
at System.Web.Mvc.Html.InputExtensions.InputHelper(HtmlHelper htmlHelper, InputType inputType, String name, Object value, Boolean useViewData, Boolean isChecked, Boolean setId, Boolean isExplicitValue, IDictionary`2 htmlAttributes)
at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value, Boolean isChecked, IDictionary`2 htmlAttributes)
at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value, IDictionary`2 htmlAttributes)
at System.Web.Mvc.Html.InputExtensions.RadioButton(HtmlHelper htmlHelper, String name, Object value)
at ASP.views_vbs_register_aspx.__RenderregisterContent(HtmlTextWriter __w, Control parameterContainer) in c:\Users\David\Documents\BellevueProject\Bellevue\BellevueTeachers\Forms\Views\VBS\Register.aspx:line 42
I just tried something that makes this work.
The problem does not occur if I do not do the validation step but of course I need the validation.
That gave me a clue for the solution.
The ValidationMessage HtmlHelper method takes a string argument that is the name of the property or model object being validated.
I just changed that name to be "gender2" as follows:
<label for="gender">Gender</label>
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.ValidationMessage("gender2") %>
And I changed the validation code to refer to this new name (even though that property does not exist, it still works):
if (!gender.HasValue)
ModelState.AddModelError("gender2", "gender required");
This works as desired.
I would have thought the other should have worked, but this is a simple workaround and I am documenting that here.
EDIT:
By the way I tried changing the gender property to a string instead of a nullable int, and the same exact problem occurs.
The work around still seems to be in using a different key name for the Validation Message.
This is very similar, if not equal to the checkbox issue: The Html.Checkbox() helper method generates a hidden field with a false value. If this field were missing, the browser would not send any data for uncheked boxes.
A radiobutton, however, is supposed to have a value, and the possible values can be more than one. In this case it is not so easy to handle the non-selection case, which I guess is the reason it isn't.
The workaround for me was to add a hidden field as follows:
<%= Html.RadioButton("gender", 1) %> Male
<%= Html.RadioButton("gender", 2) %> Female
<%= Html.Hidden("gender", null) %>
You might want to try changing gender to a string (M/F) instead of an int and see if that works.
If you absolutely must have it as an int, you could always translate on the back end.
private int? gender { get; set; }
public string displayGender
{
get
{
return this.gender.HasValue
? (this.gender.Value == 1 ? "M" : "F" )
: null;
}
set
{
this.gender = null;
if (value == "M")
this.gender = 1;
else if (value == "F")
this.gender = 2;
}
}
<label for="gender">Gender</label>
<%= Html.RadioButton("displayGender", "M") %> Male
<%= Html.RadioButton("displayGender", "F") %> Female
<%= Html.ValidationMessage("displayGender") %>
Base on your comment, you may want to add:
<%= Html.RadioButton("displayGender",
string.Empty,
true, // this is the default
new { #style = "display: none;" } ) %>
This will ensure that displayGender gets posted back (there will always be a chosen radio) and I think the value will be string.Empty instead of a null reference. If this works, you may want to try switching back to the nullable int.
Try adding the following line of code after the ModelState.AddModelError():
ModelState.SetModelValue("gender", ValueProvider["gender"]);
If you want to wire in client-side validation. Skip the html helper and hand-write your radios, giving them attributes for data-val and data-val-required. Like this.
<input type="radio" name="displayGender" id="displayGender" value="1" data-val="true" data-val-required ="The gender field is required."/>
<input type="radio" name="displayGender" id="displayGender" value="2" data-val="true" data-val-required ="The gender field is required."/>
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)%> <%= 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?
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.
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");
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.
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.
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.
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.
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");
#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!
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.
<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";
}
}
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.
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.
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.
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);
}
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>
<% } %>
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
How about something like this?
bool isChecked = false;
if (Boolean.TryParse(Request.Form.GetValues(”chkHuman”)[0], out isChecked) == false)
ModelState.AddModelError(”chkHuman”, “Nice try.”);
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.
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);
}
});
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/
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()" />
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
The code
<%=Html.CheckBox("SendEmail") %>
evaluates to two HTML elements when it's rendered
<input id="SendEmail" name="SendEmail" type="checkbox" value="true" />
<input name="SendEmail" type="hidden" value="false" />
Is this by a bug? Or by design? If it's by design, why?
I was having some trouble this morning with retrieving selected values from a checkbox group in my MVC app. I sent this out to my team and thought I'd share it with everyone else.
When posting back values for checkboxes, the standard behaviour of all browsers is to completely leave out un-checked checkboxes from the post back. So if you have a checkbox that isn’t checked then nothing shows up for this in Request.Form. This is a fairly well-known phenomenon that most developers account for.
In ASP.Net MVC when you use Html.Checkbox, it attempts to get around this and ensure that you have a value posted back (in this case ‘false’) for un-checked checkboxes. This is done by adding a hidden field containing the value ‘false’.
Eg.
<%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>
Produces the HTML
<input id="Sites" name="Sites" type="checkbox" value="1" /><input name="Sites" type="hidden" value="false" />
This is all good and well, until you attempt to use checkbox groups. That is more than one checkbox with the same name where the values are sent back in a single comma separated string.
MVC can split this string up for you automatically if you define the value as an array (string[] Sites).
Here’s the view code:
<% foreach(var s in Model) { %>
<li><%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>
<label><%= s.Name %></label>
</li>
<% } %>
The appropriate controller action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int Id, string Name, string SubmissionUrl, string[] Sites)
Unfortunately, because the Html.Checkbox produces this hidden field value as well, the resulting array contains not only values for the selected checkboxes but also ‘false’ for every un-checked checkbox. You get an array that looks something like this:
[0] 'false'
[1] 'false'
[2] '110'
[3] '50'
[4] 'false'
Where 'false' is for checkboxes that are not selected, and integers are the values for the checkboxes that are selected.
This can throw your code out quite a bit if you have only integers for the values of your checkboxes and want to define the result as an array of integers, like so:
public ActionResult Edit(int Id, string Name, string SubmissionUrl, int[] Sites)
Which results in an exception being thrown because it can’t convert the string value ‘false’ to an integer.
The solution is very simple, just avoid Html.Checkbox and manually create the checkboxes in your view code like this:
<% foreach(var s in Model) { %>
<li><input type="checkbox" name="Sites" value="<%=s.Value%>" <% if (s.Selected) { %>checked="checked"<% } %> />
<label><%= s.Name %></label>
</li>
<% } %>
Hope this helps someone else!
When all else fails, read the source code. :) this is from HtmlHelper.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'm not exactly sure how useful that is, but at least you know the intention.
I think I found something on the web that is directly related to my question.