ASP.NET MVC Collection und Checkbox Binding

Dieser Post zeigt wie eine Collection an eine View gebunden werden kann, damit eben diese Collection beim Postback, vom ModelBinder, wieder deserialisiert werden kann.
Dabei soll das boolean Property als Checkbox dargestellt werden.

Versuchsaufbau
Die Klasse “PersonModelCollection” implementiert das “IList” Interface und stellt somit unsere Collection bzw. unsere Liste dar. In dieser Liste werden n “PersonModel”s abgelegt. Diese repräsentieren eine Person.
Der “HomeController” stellt zwei Methoden zur Verfügung. “Index()” welche mit Http GET aufgerufen werden kann, die “PersonModelCollection” aufbaut und die “Index”-View zurück gibt. Die Methode “Index(Colletion)” wird mittels Http POST aufgerufen und soll wiederum die “PersonModelCollection” zurück erhalten. Die “IndexView” soll alle “PersonModel” Instanzen der “PersonModelCollection” darstellen. Dabei können verschiedene “PersonModel”s mit einer Textbox ausgewählt werden.

Die Struktur des ASP.NET MVC Projektes:
Collection_and_checkbox_binding_2

Und hier das Ganze als UML Diagramm:
Collection_and_checkbox_binding_1

Der Code des Controllers sieht wie folgt aus:

public ActionResult Index()
{
public ActionResult Index()
{
 var personModelCollection = new PersonModelCollection
 {
  new PersonModel { Id = "1", Firstname = "A" },
  new PersonModel { Id = "2", Firstname = "B" },
  new PersonModel { Id = "3", Firstname = "C" }
 };

 return View(personModelCollection);
}

[HttpPost]
public ActionResult Index(PersonModelCollection collection)
{
 return View();
}

Die View
Der erste Versuch. Die View wird mittels “foreach” aufgebaut.

@using (Html.BeginForm())
{
 foreach (var item in Model)
 {
  <div class="row">
   <div class="col-md-12">
	@Html.HiddenFor(m => item.Id)
	@Html.HiddenFor(m => item.Firstname)
	@Html.DisplayFor(m => item.Firstname)
	@Html.CheckBoxFor(m => item.IsSelected)
  </div>
 </div>
  }

<input type="submit" value="Submit" />
}
<form action="/Home/Index" method="post">
 <div class="row">
  <div class="col-md-12">
   <input id="item_Id" name="item.Id" type="hidden" value="1" />
     A
   <input id="item_IsSelected" name="item.IsSelected" type="checkbox" value="true" />
   <input name="item.IsSelected" type="hidden" value="false" />
  </div>
 </div>
 <div class="row">
  <div class="col-md-12">
   <input id="item_Id" name="item.Id" type="hidden" value="2" />
    B
   <input id="item_IsSelected" name="item.IsSelected" type="checkbox" value="true" />
   <input name="item.IsSelected" type="hidden" value="false" />
  </div>
 </div>
 <input type="submit" value="Submit" />
</form>

Die Darstellung, in der View, ist wie gewünscht.
Auf folgendem Bild ist zu sehen wie der “Postback” aussieht:
Collection_and_checkbox_binding_3
Der Modelbinder versteht die Werte im Postback jedoch nicht, die deserialisierung schlägt fehl und der Controller erhält “null” als Parameter für das CollectionModel.

Zweiter Versuch, dieses Mal mit “for”:

@using (Html.BeginForm())
{
 for (int i = 0; i < Model.Count; i++)
 {
   <div class="row">
    <div class="col-md-12">
     @Html.HiddenFor(m => Model[i].Id)
     @Html.HiddenFor(m => Model[i].Firstname)
     @Html.DisplayFor(m => Model[i].Firstname)
     @Html.CheckBoxFor(m => Model[i].IsSelected)
    </div>
   </div>
 }

 <input type="submit" value="Submit" />
}
<form action="/Home/Index" method="post">
 <div class="row">
  <div class="col-md-12">
   <input name="[0].Id" type="hidden" value="1" />
    A
   <input name="[0].IsSelected" type="checkbox" value="true" />
   <input name="[0].IsSelected" type="hidden" value="false" />
  </div>
  </div>
  <div class="row">
  <div class="col-md-12">
   <input name="[1].Id" type="hidden" value="2" />
    B
   <input name="[1].IsSelected" type="checkbox" value="true" />
   <input name="[1].IsSelected" type="hidden" value="false" />
  </div>
 </div>
</form>

Der Unterschied im generierten HTML ist deutlich zu sehen.
Denn der Name des einzelnen Input Elements ist nun nicht mehr “item.XY” sondern “[x].XY”.
Entsprechend sehen die Daten im Postback aus:
Collection_and_checkbox_binding_4

Und siehe da, dieses Mal versteht der Modelbinder was gemacht werden soll und die Collection wird richtig deserialisiert.

Fazit
Für das Binden von Collections muss eine “for” Schlaufe benutzt werden. Ansonsten kann der Postback nicht deserialisiert werden. Was wiederum bedeutet, dass nur Collections mit Indexer (z.B. IList, nicht aber ICollection) gebunden werden können.

Leave a Reply

Your email address will not be published. Required fields are marked *