MV3 Duplicate Query String Values for CheckBox (true,false for boolean)

Accepted answer is correct however in my case in a recent development the MVC behaviour is misleading.

The MVC Html.CheckBox(...) and Html.CheckBoxFor(...) generate an extra input of 'type=hidden' with the same ID as the checkbox control, leading to the duplicate URL parameters. I got around this problem by simply including the mark up desired as follows:

@if(checkTrue){
    <input type="checkbox" id="MyCheckBox" name="MyCheckbox" checked="checked">
}else{
    <input type="checkbox" id="MyCheckBox" name="MyCheckbox">
}

Would be better wrapped upin a helper to use in place of the MVC code so the value check is encapsulated.

As part of my application, the controller maintains sets of query parameters using both form injection and link injection using helpers in order to preserve state (of paging/filtering controls for example) when clicked to navigate within the same controller scope. As a result of this feature, the check box element is always set back to false if the standard MVC helpers are used. It's a good thing I noticed and did not waste much time on this bug.


This behaviour is by design of the checkbox control. The standard HTML checkbox control passes no value if it is not checked. This is unintuitive. Instead, the ASP.Net checkbox control has 2 elements, the standard control which is visible and also a hidden control with a value of 'False'.

Therefore, if the checkbox is not checked, there will be one value passed: False.
If it is checked, there will be two values, True and False. You therefore need to use the following code to check for validity in your code:

bool checkboxChecked = Request.QueryString["MyCheckBox"].Contains("True");

In my model, I had a collection of checkboxes like so:

public class PrerequisitesViewModel
{
    public List<StudentPrerequisiteStatusViewModel> PrerequisiteStatuses { get; set; }
}

public class StudentPrerequisiteStatusViewModel
{
    public long Id { get; set; }

    public string Name { get; set; }

    public bool IsSelected { get; set; }
}

In order to get everything to bind correctly, I had to actually convert the values from the querystring and parse them manually with the following code:

// fix for how MVC binds checkboxes... it send "true,false" instead of just true, so we need to just get the true
for (int i = 0; i < model.PrerequisiteStatuses.Count(); i++)
{
    model.PrerequisiteStatuses[i].IsSelected = bool.Parse((Request.QueryString[$"PrerequisiteStatuses[{i}].IsSelected"] ?? "false").Split(',')[0]);
}

Alas, it works, but I can't believe this is necessary in MVC! Hopefully, someone else knows of a better solution.