ASP.NET MVC4 FormCollection.GetValues not returning correct values

Karoline Brynildsen picture Karoline Brynildsen · Jul 16, 2013 · Viewed 10.2k times · Source

I'm building a mobile webiste using ASP.NET MVC4. One of the pages in my solution contains a table where all the rows in the table can be edited and saved in one batch. To do this I am using FormCollection in the Action in the Controller. This works as expected for most of the fields, exept one: item.IsPercent. This field is a boolean, and FormCollection.GetValues("item.IsPercent") returns twice the number of rows in the table and the list always alternates between true and false, no matter what the actual values are.

How can I collect the right value for the Percent checkboxes?

An Example

enter image description here

My View

@using (Html.BeginForm()) {
 @Html.AntiForgeryToken()
@Html.ValidationSummary(true)

foreach (var group in Model.GroupBy(i => i.Exercise.Name)) {
<table>
<tr>
        <th colspan="4">@Html.Label(group.Key)</th>
</tr>
    <tr>
        <td>Load</td>
        <td>Percent</td>
        <td> </td>
        <td>Reps</td>
    </tr>
 @foreach (var item in group.OrderBy(i => i.Order))
    {

     @Html.HiddenFor(modelItem => item.Id)
    <tr>

        <td>
            @Html.EditorFor(modelItem => item.Load) 
            @Html.ValidationMessageFor(modelItem => item.Load)
        </td>
        <td>
            @Html.EditorFor(modelItem => item.IsPercent)
        </td>
        <td>
            x
        </td>
        <td>
            @Html.EditorFor(modelItem => item.Repetitions)
             @Html.ValidationMessageFor(modelItem => item.Repetitions)
        </td>
    </tr>
    }
    </table>
    <br />
}

<p>
    <input type="submit" value="Save" />
</p>
}

My Controller

[HttpPost]
    public ActionResult EditProgramTemplateLines(FormCollection c)
    {
        int i = 0;
        int ptId = 0;
        if (ModelState.IsValid)
        {
            var ptIdArray = c.GetValues("item.id"); // [1,2,3,4]
            var ptLoadArray = c.GetValues("item.Load"); // [50, 55, 60, 80]
            var ptPercentArray = c.GetValues("item.IsPercent"); // [true, false, true, false, true, false, true, false]
            var ptRepsArray = c.GetValues("item.Repetitions"); // [13, 10, 5, 2]

            for (i = 0; i < ptIdArray.Count(); i++) {
                var ptLine = factory.GetProgramTemplateLine(Convert.ToInt32(ptIdArray[i]));
                if(ptId == 0)
                    ptId = ptLine.ProgramTemplateId;

                ptLine.Load = ConvertToDouble(ptLoadArray[i]);
                ptLine.IsPercent = Convert.ToBoolean(ptPercentArray[i]);
                ptLine.Repetitions = ConvertToInt(ptRepsArray[i]);
                factory.SetEntryAsModified(ptLine);

            }
            factory.SaveChanges();
            return RedirectToAction("Details", new { id = ptId });
        }
        return View();
    }

Updated solution Considering the link posted in comment below FormCollection for a checkbox my solution is to loop throug the array like this:

var percentId = 0;
for(i = 0;i<ptIdArray.Count();i++){
if(ptPercentArray[percentId] == true){
item.IsPercent = true;
percentId = percentId + 2;
}
else{
item.IsPercent = false;
percentId++;
}
}

Answer

Fals picture Fals · Jul 16, 2013

Take a look at the HTML generated for the Html.EditorFor! The helpers create one checkbox and one hidden field for each checkbox to make state management easily at server side! Thats the problem! Using FormCollection as parameter you need to differentiate the hidden from the real checkbox to get the value!

Hopes this help you!