ASP.NET Listview multiselect Checkbox

Ein 10 Jahre alter und noch immer funktionierenden ASP.NET Webforms Code und es muss die Business Logik geändert werden. Nun war der damalige Entwickler ein Genie seines Faches und hat alles eingebaut was Stand der Technik war. SOAP basierter ASMX Service, Server Code, Client seitige Validierung mit Callbacks und so weiter. Im Grund nach ist der Anwendungsfall ein spezifisches Warenkorb Problem.

Also habe ich den Entschluss gefasst: sanfte Modernisierung unter anderem basierend auf ASP.NET Webforms model binding einzusetzen. Als prototypische Implementierung wird eine Liste genommen. Durch Checkboxen können mehrere Einträge gleichzeitig ausgewählt werden. Dabei wird adhoc die Summe gebildet und angezeigt. Beim senden der Auswahl durch ein Form Post wird am Server per ASP.NET die ausgewählten Einträge bearbeitet.

image

So banal das Szenario klingt, so komplex ist die Lösung. Technisch muss man zwei Programme schrieben und dann sicher stellen das diese miteinander kommunizieren. Um ehrlich zu sein wird hier ein SPA Ansatz mit z.B. Angular.js vermutlich die einfachere Lösung sein. Aber auch hier braucht man quasi zwei Programme. Die UI Logik und die REST Service Logik. Einzig die Kommunikation per Json ist quasi standardisiert.

Im Kontext einer existierenden Webforms Anwendung ist aber ASP.NET basierende Lösung zumindest ins Auge zu fassen.

Zunächst wird ein Model erstellt. In der Praxis ist die entsprechende Klasse ein Abfallprodukt eines Entity Framework models und erfordert eben kein manuelles coding. Hier wird die VB.NET Model Klasse per Hand erstellt.

   1:  Public Class daten
   2:      Property id As Integer
   3:      Property text As String
   4:      Property preis As Integer
   5:      Property checked As Boolean
   6:  End Class

Wer Listen benötigt nutzt gerne Controls wie Repeater, Listiew oder Gridview. Allen gemein ist ein HTML Template System. So wird im Itemtemplate der HTML Code defindiert der für jeden Listeneintrag erstellt werden soll. Ich wähle das Listview Steuerelement, weil es erlaubt die ID des Records über ein Keyattribut mitzuführen. Das wird für die Mehrfachselektion der Liste durch Checkboxen noch wichtig.

Modelbing erlaubt es mit typisierten Feldern zu arbeiten. Statt Databinder.eval oder später auch nur eval, wird schlicht item.Property gebunden. Dazu ist aber nötig den Modelklassennamen im Itemtype zu deklarieren. Etwas später wird noch erläutert, wie man die Daten lädt.

   1:    <asp:Listview ID="Repeater1" runat="server" ItemType="test5.daten"
   2:              SelectMethod="Repeater1_GetData"  DataKeyNames="ID" 
   3:              >
   4:              <ItemTemplate>
   5:   <asp:CheckBox Checked="<%#Item.checked %>" runat="server" 
CssClass="check_list" val="<%#item.preis %>" onclick="basketChanged()"/>
   6:                  <%#Item.ID %>-
   7:                  <%#Item.text %>-
   8:                  <%#item.preis %>
   9:                  <br />
  10:              </ItemTemplate>
  11:    </asp:Listview>
  12:        

Wenden wir uns der Checkbox zu. Ein ASP.NET Server Control erzeugt zur Laufzeit gar verwunderlichen HTML Code. Deshalb können auch Attribute wie val oder onclick belegt und genutzt werden, die eigentlich nicht exisitieren. ASP.NET rendert die sozusagen einfach durch. Dies ist erst später in ASP.NET hinzugekommen. Aus dem Server Control wird so folgender HTML Code am Server gerendert

image

Der Wert von Val findet sich im Span Element und onclick in dem HTML Input Element. Das Attribut Class wird dann per JavaScript genutzt um im DOM die Checkbox zu finden. Es kommt die JavaScript Bibliothek Jquery zum Einsatz. Jedes HTML Element mit der Klasse “check_list” wird gesucht und das erste darunter liegende Input Feld auf checked überprüft. Wenn gefunden wird der Wert aus Val aufsummiert und einem DIV mit der ID Summe zugewiesen.

   1:          function basketChanged() {
   2:              var sum = 0;
   3:              $('.check_list').each(function () {
   4:                  if ($(this).find('input')[0].checked == true) {
   5:                      sum += Number($(this).attr("val"));
   6:                  }
   7:              });
   8:              $("#summe").html(sum);
   9:          }

Das ist eine sehr sparsam implementiere client seitige Logik die auch ohne JQuery Bibliothek auskommen würde.

Wie versprochen noch der Code um die Daten per Model Binding zu laden. Der Methoden Rumpf wird am besten aus der ASPX Seite erzeugt. Man schreibt den Attribut Namen SelectMethod und lässt dann Visual Studio Intellisense sein Werk verrichten.

image

   1:    Public Function Repeater1_GetData() As IEnumerable(Of daten)
   2:          Dim d As New List(Of daten)
   3:          d.Add(New daten() With {.id = 1, .text = "Text1", .preis = 12, .checked = False})
   4:          d.Add(New daten() With {.id = 2, .text = "Text2", .preis = 9, .checked = False})
   5:          d.Add(New daten() With {.id = 3, .text = "Text3", .preis = 112, .checked = False})
   6:          d.Add(New daten() With {.id = 4, .text = "Text4", .preis = 232, .checked = False})
   7:          d.Add(New daten() With {.id = 5, .text = "Text5", .preis = 2, .checked = True})
   8:          Return d
   9:      End Function

Im nächsten Schritt drückt der Benutzer einen ASP.NET Button und möchte die Auswahl z.b. in einer Datenbank speichern. Hier werden die Eigenheiten von HTML schlagend. Nur gecheckte Input Elemente werden per Post übermittelt. Der Wert ist “On”.

image

Die Web Seite verliert nach dem rendern auch seinen Status und alle Daten. Diese müssen somit mühsam wieder hergestellt werden aus den Informationen die per POST geliefert werden. Das ist nicht ganz einfach aber ASP.NET übernimmt mit seinem Hidden Viewstate Feld ein Stück der Arbeit.

Die Daten werden von ASP.NET im Control wieder hergestellt, allerdings in der Form wie der DOM Tree erstellt wurde und nicht in den ursprünglichen generischen Liste die dann per Dataitem ausgelesen werden könnte. Folglich muss Reihe für Reihe durchiteriert werden und dann wiederrum Spalte für Spalte. Wenn es sich um eine Checkbox handelt, wird ein Typ Cast durchgeführt und der Status Checked ausgelesen. Fall der Haken gesetzt ist, erhält man so den Index der Reihe und kann mit diesem den DataKey aus der Liste auslesen. Dieser stellt die ID dar und lässt sich dann per Datenbank Operation nutzen.

   1:  Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
   2:     For i As Integer = 0 To Repeater1.Items.Count - 1
   3:         Dim irow As Integer = Repeater1.Controls(i).Controls.Count
   4:         For col As Integer = 0 To irow - 1
   5:                  If Repeater1.Controls(i).Controls(col).GetType().Name = "CheckBox" Then
   6:                      Dim chkTemp As CheckBox = 
CType(Repeater1.Controls(i).Controls(col), System.Web.UI.WebControls.CheckBox)
   7:                      If chkTemp.Checked Then
   8:                          Dim id = Repeater1.DataKeys(i).Value
   9:   
  10:                      End If
  11:                  End If
  12:              Next
  13:          Next
  14:  End Sub

In Zeile 9 würde der Datenbank oder Entity Framework Code eingefügt um die Auswahl in der Datenbank zu persistieren.

Kommentare sind geschlossen