Lab moderne ASP.NET Webforms TODO Anwendung

Diese Übungseinheit zeigt wie man eine TODO Liste Web Anwendung mit ASP.NET 4.5 erstellt. Es können neue Aufgaben erzeugt und wieder gelöscht werden. Dabei kommen zum Einsatz Model Binding eines Repeater Controls. Als Datenbank wird ein einfaches Textfile verwendet. Für die UI Gestaltung kommt das CSS Framework Bootstrap zum Einsatz. Mit dem Updatepanel werden Ajax Callbacks ausgeführt, so das das Verhalten einer SPA Web App entspricht.

image

Programmiersprache C# Dauer ca 45 Minuten

Benötigt:Visual Studio

Level: 100

Visual Studio Web Site anlegen

Starten Sie Visual Studio 2017 und erstellen Sie einen Website –Menü Datei- Neu –WebSite. Wählen Sie aus den Templates Website für ASP.NET Webforms. Nennen Sie die Website TODO und bestätigen den Dialog mit OK.

image

Es wird eine Website angelegt mit Verzeichnissen, wie Script, Content oder App_code. Löschen Sie die Dateien

  • Site.mobile.master
  • ViewSwitcher.ascx

Fügen Sie dem Projekt in Visual Studio eine neue ASPX Datei hinzu. Rechtsclick auf Projektname im Projektmappen Explorer ( in der Regel rechts zu finden)- hinzufügen- neues Element hinzufügen – Webformular. Geben Sie der Datei den Namen todo.aspx und wählen Sie die beiden Optionen rechts unten Gestaltungsvorlage und Code in separater Datei.

image

Es erscheint der Editor in der HTML Ansicht. Im Projektmappen Explorer wurden zwei Dateien angelegt. Die HTML Sicht als ASPX und der Codebind als aspx.cs

image

Fügen Sie zwischen den öffnenden und schließenden Content Tag das Bootstrap Gerüst für 1 Zeile und 2 Spalten ein.

   1:  <div class="row">
   2:          <div class="col-md-6">
   3:   
   4:          </div>
   5:          <div class="col-md-6">
   6:   
   7:          </div>
   8:  </div>

Visual Studio unterstützt bei ASP.NET Webforms eine rudimentäre WYSIWYG Darstellungen. Wechseln sie per Tab Entwurf in den Entwurfsmodus. Ziehen Sie in die erste Zeile aus der Toolbox/Werkezugleiste ( in der Regel links) eine Texbox und einen Button. In die zweite Zeile das Repeater Steuerelement.

image

Wechseln Sie in die Ansicht Quelle. Um die optischen Fähigkeiten von Bootstrap zu nutzen wird das Formular um CSS Klassen ergänzt. Die Server Controls werden so in DIV Elemente mit CSS Klassen eingebettet. Zusätzlich wird über das Attribut cssClass der ASP.NET Servercontrols die gerenderten CSS Klassen festgelegt.

   1:  <fieldset>
   2:        <legend>TODO Items einfügen</legend>
   3:        <div class="form-group">
   4:           <label class="control-label" for="textinput">Aufgabe</label>
   5:           <asp:TextBox ID="TextBox1" runat="server" CssClass="form-control"></asp:TextBox>
   6:        </div>
   7:        <div class="form-group">
   8:            <asp:Button ID="Button1" runat="server" Text="speichern" CssClass="btn btn-primary" />
   9:        </div>
  10:  </fieldset>

Um die optische Formatierung im Editor herzustellen drücken Sie [STRG] [K]  halten die [STRG] Taste fest und drücken [D].

Betrachten Sie das Ergebnis im Browser – rechte Maustaste – view in Browser.

image

Wechseln Sie im Editor auf die Ansicht Entwurf und implementieren Sie das Click Event indem Sie den Speichern Button doppelclicken.

image

Der Editor wechselt in die Code Ansicht und zeigt folgenden C# Ereignis Code an

   1:   protected void Button1_Click(object sender, EventArgs e)
   2:      {
   3:   
   4:      }

Die Daten werden in eine TextDatei im App_data Verzeichnis gespeichert. Dieses ist vor Download durch den Benutzer geschützt. Um unabhängig vom Installationsort der Website zu sein wird die Hilfsmethode Mappath genutzt. Ergänzen Sie die Code in Zeile 2 wie in der Abbildung ersichtlich.

image

Da der Namensraum für die File Klasse fehlt muss dieser per using dem Projekt hinzugefügt werden. Visual Studio erledigt dies für Sie, wenn eine rote unterwellte Stelle angeklickt wird und dann [STRG]+[.] gedrückt wird.

Rufen Sie die Anwendung im Browser auf. Entweder per “View in Browser” oder per Debugger mit F5 starten. Wenn Fehler auftreten hilft der Debugger.

Geben Sie in der Web Anwendung die Werte “eins”, “zwei”, “drei” ein und drücken jeweils den speichern Button.

Wechseln Sie zu Visual Studio und drücken im Projektmappen Explorer refresh. Es erscheint im APP_DATA Verzeichnis eine neue Datei, die per Doppelklick geöffnet wird.

image

In der Datei stehen untereinander die Werte eins, zwei,drei.

Model Binding Repeater

Für das Model Binding wird eine Klasse TODO.cs benötigt, die unbedingt im APP_CODE Verzeichnis angelegt werden muss, da wir kein Webprojekt sondern eine Website haben.

Rechtsklick Order App_Code- hinzufügen-Klasse

image

Die Klasse bekommt zwei Propertys, ID und Aufgabe. Am schnellsten erledigen Sie die aufgabe, wenn Sie im Code prop tippen und dann [TAB] [TAB]

   1:  public class ToDo
   2:  {
   3:      public ToDo()
   4:      {
   5:          //
   6:          //TODO: Hier Konstruktorlogik hinzufügen
   7:          //
   8:      }
   9:      public int ID { get; set; }
  10:      public string Aufgabe { get; set; }
  11:   

Erstellen Sie nun das Projekt komplett neu.  Menü oben – erstellen- Projektmappe neu erstellen.

Wechseln Sie in den Editor der ASPX Seite und ergänzen Sie das Attribut Itemtype um die Klasse ToDo und wählen die SelectMethod aus. Da keine vorhanden ist, erscheint nach dem =” der Vorschlag eine Methode anzulegen.

image

Bestätigen Sie den Dialog und wechseln in zum C# Source Code der Seite. Wenn die Reiter noch offen sind sollte das darüber am einfachsten sein.

image

Die Methode zum Füllen der Daten ist nun von Visual Studio als Prototyp angelegt worden und kann mit der Logik gefüllt werden.

   1:   public IEnumerable<ToDo> Repeater1_GetData()
   2:      {
   3:          return null;
   4:     

Die Werte werden eingelesen und einem Array von Items abgelegt. Um den Items eine ID hinzuzufügen wird per ForEach Iterator eine neue generische Liste von Typ ToDo angelegt und diese zurück gegeben.

   1:   public IEnumerable<ToDo> Repeater1_GetData()
   2:      {
   3:          var todoList = new List<ToDo>();
   4:          try
   5:          {
   6:              var items = File.ReadAllLines(Server.MapPath(@"~\app_data\todoitems.txt"));
   7:              int id = 0;
   8:              foreach (string s in items)
   9:              {
  10:                  todoList.Add(new ToDo() { ID = id, Aufgabe = s });
  11:                  id++;
  12:              }
  13:          }
  14:          catch
  15:          {
  16:          }
  17:          return todoList;

Allerdings wird so noch nichts angezeigt im Browser, da die deklarative HTML Logik noch fehlt. Die Daten Steuerelement agieren wie ein foreach als Iterator durch die Datensätze. In verschiedenen Templates wird dazu der HTML Code definiert und per Item oder Binditem das Model gebunden.

Wechseln Sie in die HTML Code Ansicht der ASPX Seite und fügen Sie zwischen öffnenden und schliessenden Tags des Repeater Controls folgenden Code ein

   1:  <ItemTemplate>
   2:        <li class="list-group-item">
   3:            <div class="row">
   4:                      <div class="col-md-2"><%#Item.ID%></div>
   5:                      <div class="col-md-6"><%#Item.Aufgabe %></div>
   6:            </div>
   7:      </li>
   8:  </ItemTemplate>

Lassen Sie sich dabei von Intellisense helfen und tippen nicht alles. So muss auch der Typ nach Item. erscheinen wie in der DoTo.cs definiert.

image

Betrachten Sie das Ergebnis im Browser per Debugger F5 oder View im Browser

image

Wenn Sie nun neue Aufgaben hinzuf��gen, werden diese in die Textdatei eingefügt aber nicht angezeigt. Dies liegt daran, das ASP.NET Webforms per Viewstate die Daten rekonstruiert und nicht neu lädt. Dies muss explizit angefordert werden.

Ergänzen Sie die Methode zum speichern (button1_click) in der letzten Zeile um eine DataBind Methode auf dem Datencontrol, hier dem Repeater.

image

Testen Sie im Browser das Ergbnis.

Löschen per LinkButton Repeater

Im nächsten Schritt wird ein Funktion zum löschen der erledigten Aufgaben ergänzt. In jeder Zeile soll vorne ein X angezeigt werden. Dies lässt sich mit einem Button oder LinkButton erledigen. Allerdings ist ASP.NET Webforms typisch der Button Bestandteil des Repeaters. Ein Button im Itemtemplate eines Repeaters löst also kein Click Event korrekt aus.

Zunächst wird der Repeater bzw das ItemTemplate um die Aktion Löschen ergänzt und dem Linkbutton die per CommandArgument die ID mitgegeben und  ein Commandname.

   1:       <div class="col-md-1">
   2:            <asp:LinkButton ID="HyperLink1" runat="server"
   3:                                      CommandName="Click"
   4:                                      CommandArgument="<%#Item.ID %>">
   5:                   <span Class="glyphicon glyphicon-remove"></span>
   6:            </asp:LinkButton>
   7:        </div>
   8:                         

Nun wird noch dem Repeater das ItemCommand Event ergänzt.

   1:   <asp:Repeater ID="Repeater1" runat="server"
   2:                  ItemType="ToDo"
   3:                  SelectMethod="Repeater1_GetData"
   4:                  OnItemCommand="Repeater1_ItemCommand"
   5:             

 

Wechseln Sie im Editor in den C# Code und ergänzen die C# Logik der Repeater1_ItemCommand Methode. Die Daten werden wie vor aus dem Textfile in ein Array gelesen. Der zu löschende Datensatz wird per CommandArgument ID übergeben. Mit einer LINQ Query wird ein Filter angelegt und alle anderen Werte in ein neues Array kopiert und dieses wieder in die Datei geschrieben.

   1:  var items = File.ReadAllLines(Server.MapPath(@"~\app_data\todoitems.txt"));
   2:  var id = Convert.ToInt32(e.CommandArgument.ToString());
   3:  var neuitems = items.Where(x => x != items[id]);
   4:  File.WriteAllLines(Server.MapPath(@"~\app_data\todoitems.txt"),
   5:       neuitems);
   6:  Repeater1.DataBind();

Vergessen Sie nicht DataBind auf dem Repeater Control auszuführen um sicherzustellen das die Methode Repeater1_GetData die Anzeige auch aktualisiert.

Per Click auf das X verschwindet der Eintrag.

image

UpdatePanel + Repeater wie SPA

Sie werden sicher beobachtet haben wie die Seite beim Reload flackert. Es wird der ganze HTML Seitencode vom Server gesendet und vorm Browser jedes mal komplett neu aufgebaut. Eine einfache Abhilfe schafft das UpdatePanel Control. Der dynamisch auszutauschende Teil wird vom Updatepanel bzw seinem Contenttemplate umschlossen. Es ist auch möglich und sinnvoll mehrere Updatepanels zu nutzen. Außenliegende Buttons bzw. andere Controls können als Trigger fürs update dienen.

Wechseln Sie in Visual Studio in den Editor der ASPX Seite. Klappen Sie per Minus Zeichen links beide Bereiche <div class-“col-md-6 ein. Schreiben Sie umschließend das Updatepanel und das Contenttemplate.

image

Der Trigger dient nur zur Anschauung, ist auskommentiert und wird weggelassen.

Betrachten Sie die todo.aspx Seite im Browser oder starten es per F5 im Debugger. Legen Sie neue Aufgaben an. Der Browser darf nun nicht mehr dabei flackern.

Allerdings wird beim löschen nach dem drücken des Linkbuttons im Repeater ein kompletter Postback im Updatepanel ausgeführt. Man erkennt dies auch in den Browser Tools (F12) –Network – Headers
an den gesendetem HTTP Header. Diese Abbildung stellt den Flackerfreien Save Button XMLHttpRequest dar.

image

Bonus Track

Der Grund liegt am manuell aufgerufenen DataBind. Im Kern besteht ASP.NET Webforms aus sehr viel Magie um ein Verhalten wie eine Windows Forms Anwendung nachzustellen. Databind baut nicht nur die Datenbindung neu auf, sondern auch die Events. So muss manuell dafür gesorgt werden, das diese wieder stimmen. Je nach Steuerelement ist das passende Event das RowDataBound (Datagrid) oder ItemDataBound (Repeater). Darin wird jedes Control manuell dem ScriptManager als PostbackControl hinzugefügt.

   1:   protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
   2:      {
   3:   
   4:          foreach (RepeaterItem item in Repeater1.Items)
   5:          {
   6:              LinkButton lnkFull = item.FindControl("HyperLink1") as LinkButton;
   7:              ScriptManager.GetCurrent(this).RegisterPostBackControl(lnkFull);
   8:          }
   9:   
  10:      }
Kommentare sind geschlossen