7. Serverkomponenten ASP – 1
7.1. Introduction
In diesem Kapitel beschreiben wir die in ASP.NET empfohlene Technologie zum Aufbau der Benutzeroberfläche. Wir wissen, dass es bei der Verarbeitung einer .aspx-Seite durch den Webserver zwei klar voneinander getrennte Phasen gibt:
- Zunächst wird der Controller der Seite ausgeführt. Dieser besteht aus Code, der sich entweder in der .aspx-Seite selbst (Lösung WebMatrix) oder in einer separaten Datei (Visual-Lösung Studio.NET) befindet.
- Anschließend wird der Darstellungscode der .aspx-Seite ausgeführt, um in den Code HTML umgewandelt und an den Client gesendet zu werden.

ASP.NET bietet drei Tag-Bibliotheken zum Schreiben des Darstellungscodes der Seite:
- die klassischen HTML-Tags. Diese haben wir bisher verwendet.
- die Server-Tags HTML
- die Webforms-Tags
Unabhängig davon, welche Tag-Bibliothek verwendet wird, ändert sich die Rolle des Seiten-Controllers nicht. Er muss den Wert der dynamischen Parameter berechnen, die im Präsentationscode vorkommen. Bislang waren diese dynamischen Parameter einfach: Es handelte sich um Objekte vom Typ [String]. Wenn also im Präsentationscode ein Tag <%=name%> vorkommt:
- erklärt der Seiten-Controller eine Variable vom Typ [nom] und berechnet deren Wert
- Wenn der Seiten-Controller seine Arbeit beendet hat und der Präsentationscode ausgeführt wird, um die Antwort HTML zu generieren, wird das Tag <%=nom%> durch den vom Controller-Code berechneten Wert ersetzt
Es ist bekannt, dass die Trennung zwischen Controller und Darstellung willkürlich ist und dass Controller-Code und Darstellungscode auf derselben Seite gemischt werden können. Wir haben erläutert, warum von dieser Methode abgeraten wird, und werden uns weiterhin an die Trennung zwischen Controller und Darstellung halten.
Die Tags [HTML serveur] und [WebForms] ermöglichen es, komplexere Objekte als das einfache Objekt [String] in den Präsentationscode einzufügen. Dies ist manchmal von echtem Interesse. Nehmen wir das Beispiel eines Formulars mit einer Liste. Diese soll dem Kunden mit einem HTML-Code präsentiert werden, der etwa so aussieht:
<select name="uneListe" size="3">
<option value="opt1">option1</option>
<option value="opt2">option2</option>
<option value="opt3" selected>option3</option>
</select>
Der Inhalt der Liste und die auszuwählende Option sind dynamische Elemente und müssen daher vom Seiten-Controller generiert werden. Wir sind bereits auf dieses Problem gestoßen und haben es gelöst, indem wir im Layout-Code das Tag
Dieses Tag wird durch den Wert [String] der Variablen [uneListeHTML] ersetzt. Dieser vom Controller berechnete Wert muss der Code HTML aus der Liste c.a.d sein: „<select name=..>...</select>“. Das ist nicht besonders schwierig umzusetzen und scheint eine elegante Lösung zu sein, die es vermeidet, Generierungscode direkt in den Darstellungsteil der Seite einzufügen. Hier müsste eine Schleife mit Prüfungen eingefügt werden, wodurch dieser Teil stark „verunreinigt“ würde. Dennoch hat diese Methode einen Nachteil. Die Trennung von Steuerung und Darstellung einer Seite dient auch dazu, zwei Zuständigkeitsbereiche abzugrenzen:
- den des Entwicklers .NET, der sich um den Seiten-Controller kümmert,
- den des Grafikdesigners, der sich um den Darstellungsteil der Seite kümmert
Hier sieht man, dass die Generierung des Codes HTML für die Liste in den Controller verlagert wurde. Der Grafikdesigner möchte möglicherweise auf diesen Code HTML zugreifen, um das „visuelle“ Erscheinungsbild der Liste zu ändern. Er wäre dann gezwungen, im Bereich [contrôleur] zu arbeiten und somit seinen Zuständigkeitsbereich zu verlassen, was das Risiko von versehentlich in den Code eingeschleppten Fehlern mit sich bringt.
Server-Tag-Bibliotheken lösen dieses Problem. Sie stellen ein Objekt bereit, das eine Liste HTML repräsentiert. So bietet die Bibliothek [WebForms] das folgende Tag an:
Dieses Tag repräsentiert ein Objekt vom Typ [ListBox], das vom Seiten-Controller bearbeitet werden kann. Dieses Objekt verfügt über Eigenschaften, um die verschiedenen Optionen der Liste HTML darzustellen und die ausgewählte Option zu kennzeichnen. Der Seiten-Controller weist diesen Eigenschaften daher die entsprechenden Werte zu. Bei der Ausführung des Darstellungsteils wird das Tag
durch den Code HTML ersetzt, der das Objekt [uneListe], c.a.d darstellt. Der Code „<select ..>...</select>“. Bislang gibt es keinen grundlegenden Unterschied zur vorherigen Methode, abgesehen von einer objektorientierten Programmierweise, was interessant ist. Kehren wir zu unserem Grafikdesigner zurück, der das „Aussehen“ der Liste ändern muss. Die Server-Tags verfügen über Stilattribute (BackColor, Bordercolor, BorderWidth, ...), mit denen sich das Erscheinungsbild des entsprechenden Objekts HTML festlegen lässt. So könnte man schreiben:
<asp:ListBox id="ListBox1" runat="server" BackColor="#ffff99"></asp:ListBox></P>
Der Vorteil besteht darin, dass der Grafikdesigner für diese Änderungen im Präsentationscode bleibt. Dies ist ein klarer Vorteil gegenüber der vorherigen Methode. Die Server-Tag-Bibliotheken erleichtern somit die Erstellung des Präsentationsteils der Seiten, den wir in den vorangegangenen Kapiteln als Benutzeroberfläche bezeichnet haben. Ziel dieses Kapitels ist es, diese Bibliotheken vorzustellen. Wir werden sehen, dass sie manchmal komplexe Objekte wie Kalender oder mit Datenquellen verknüpfte Tabellen bieten. Sie sind erweiterbar, c.a.d, sodass der Benutzer seine eigene Tag-Bibliothek erstellen kann. So kann er beispielsweise ein Tag erstellen, das ein Banner auf einer Seite generiert. Alle Seiten, die dieses Tag verwenden, verfügen dann über dasselbe Banner.
Die für ein Tag erstellte Ausgabe HTML passt sich dem Typ des Client-Browsers an. Wenn der Browser eine Anfrage an den Webserver sendet, übermittelt er unter seinen Headern HTTP einen Header [User-Agent: xx], wobei [xx] den Client identifiziert. Hier ein Beispiel:
Anhand dieser Information kann der Webserver die Fähigkeiten des Clients ermitteln, insbesondere den Typ des Codes HTML, den dieser verarbeiten kann. Im Laufe der Zeit gab es nämlich mehrere Versionen der Sprache HTML. Aktuelle Browser verstehen die neuesten Versionen der Sprache, was bei älteren Browsern nicht der Fall ist. Je nach dem Header HTTP [User-Agent:], den der Client an den Server gesendet hat, sendet der Server ihm eine Version HTML, die er verstehen kann. Das ist eine interessante und nützliche Idee, da sich der Entwickler so keine Gedanken über den Typ des Client-Browsers seiner Anwendung machen muss.
Schließlich ermöglichen weiterentwickelte IDE-Tools wie Visual Studio.NET, WebMatrix usw. eine Gestaltung der Weboberfläche im „Windows-Stil“. Diese Tools sind zwar nicht unbedingt erforderlich, bieten dem Entwickler jedoch eine entscheidende Hilfe. Dieser entwirft die Weboberfläche mithilfe grafischer Komponenten, die er auf dieser Oberfläche platziert. Er hat direkten Zugriff auf die Eigenschaften jeder einzelnen Komponente der Oberfläche und kann diese somit nach Belieben konfigurieren. Diese Eigenschaften werden im HTML-Code zur Darstellung der Benutzeroberfläche in Attribute des <asp:>-Tags der jeweiligen Komponente übersetzt. Der Vorteil für den Entwickler besteht darin, dass er sich weder die Liste noch die Syntax der Attribute jedes einzelnen Tags merken muss. Dies ist ein erheblicher Vorteil, wenn man die von ASP.NET angebotenen Server-Tag-Bibliotheken nicht vollständig beherrscht. Sobald diese Syntax verinnerlicht ist, ziehen es manche Entwickler möglicherweise vor, die Tags direkt in den Darstellungscode der Seite zu schreiben, ohne die Phase der grafischen Gestaltung zu durchlaufen. Ein IDE ist dann nicht mehr erforderlich. Ein einfacher Texteditor reicht aus. Je nach Arbeitsweise liegt der Schwerpunkt dann entweder auf den Komponenten (Verwendung eines IDE) oder auf den Tags (Verwendung eines Texteditors). Diese beiden Begriffe sind gleichbedeutend. Die Komponente ist das Objekt, das vom Steuerungscode der Seite bearbeitet wird. Das IDE ermöglicht uns in der Entwurfsphase den Zugriff auf ihre Eigenschaften. Die diesen Eigenschaften zugewiesenen Werte werden sofort in die Attribute des Tags der Komponente im Präsentationscode übersetzt. In der Ausführungsphase bearbeitet der Steuerungscode der Seite die Komponente und weist bestimmten Eigenschaften Werte zu. Der Präsentationscode generiert daraufhin den Code HTML der Komponente, wobei er einerseits die bei der Gestaltung festgelegten Attribute für das entsprechende Server-Tag und andererseits die vom Steuerungscode berechneten Werte der Komponenteneigenschaften verwendet.
7.2. Der Ausführungskontext der Beispiele
Wir werden die Gestaltung von Webschnittstellen auf Basis von Serverkomponenten anhand von Programmen veranschaulichen, deren Ausführungskontext meist wie folgt aussieht:
- Die Webanwendung besteht aus einer einzigen Seite P, die ein Formular F enthält,
- der Client stellt seine erste Anfrage direkt an diese Seite P. Dies geschieht durch Aufruf der URL der Seite P mit einem Browser. Es handelt sich also um eine Anfrage GET, die an diese URL P gerichtet wird. Der Server liefert die Seite P und damit das darin enthaltene Formular F aus,
- der Benutzer füllt dieses aus und sendet es ab (c.a.d), d. h., er führt eine Aktion aus, die den Browser dazu veranlasst, das Formular F an den Server zu senden. Der Vorgang POST des Browsers ist weiterhin an die Seite P gerichtet. Der Server liefert erneut die Seite P mit dem Formular F aus, dessen Inhalt durch die Aktion des Benutzers möglicherweise geändert wurde.
- Anschließend werden die Schritte 2 und 3 fortgesetzt.
Es handelt sich um einen ganz besonderen Ausführungsprozess, außerhalb dessen bestimmte, im Folgenden erläuterte Konzepte nicht mehr funktionieren. Wir befinden uns nicht mehr in einem Kontext der MVC-Architektur, in der eine mehrseitige Anwendung von einer bestimmten Seite gesteuert wird, die wir als „Anwendungscontroller“ bezeichnet haben. In dieser Art von Architektur zielen die POST-Formulare auf den Controller und nicht auf die Formulare selbst ab. Wir werden jedoch sehen, dass die Erstellung eines Formulars mit Serverkomponenten bedeutet, dass dieses Formular an sich selbst gesendet wird.
7.3. Die Label-Komponente
7.3.1. Verwendung
Mit dem Tag <asp:label> lässt sich dynamischer Text in den Darstellungscode einer Seite einfügen. Es leistet also nicht mehr als das bisher verwendete Tag <%=variable%>. Die Untersuchung dieses ersten Tags ermöglicht es uns, den Mechanismus der Server-Tags zu verstehen. Wir erstellen eine Seite mit einem Steuerungsteil [form1.aspx.vb] und einem Darstellungsteil [form1.aspx]. Es geht darum, die Uhrzeit anzuzeigen:

Dieses Problem wurde bereits in Kapitel 2 behandelt, und der Leser wird gebeten, dort nachzuschlagen, wenn er wissen möchte, wie es gelöst wurde. Der Darstellungscode [form1.aspx] lautet wie folgt:
<%@ page src="form1.aspx.vb" inherits="form1" AutoEventWireup="false" %>
<HTML>
<HEAD>
<title>Webforms</title>
</HEAD>
<body>
<asp:Label Runat="server" ID="lblHeure" />
</body>
</HTML>
Wir fügen das Tag <asp:label> ein. In den Tag-Bibliotheken ist das Attribut [runat="server"] obligatorisch. Das Attribut ID identifiziert die Komponente. Der Controller muss mit dieser Kennung darauf verweisen. Der Code des Controllers [form1.aspx.vb] lautet wie folgt:
Imports System.Web.UI.WebControls
Public Class form1
Inherits System.Web.UI.Page
Protected lblHeure As Label
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Init
' speichert die aktuelle Abfrage unter request.txt im Seitenordner
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Die Uhrzeit wird in lblHeure gespeichert
lblHeure.Text = "Il est " + Date.Now.ToString("T")
End Sub
End Class
Der Controller muss dem Objekt [lblHeure] vom Typ [System.Web.UI.WebControls.Label] einen Wert zuweisen. Alle Objekte, die durch die <asp:>-Tags angezeigt werden, gehören zum Namensraum [System.Web.UI.WebControls]. Daher kann dieser Namensraum systematisch importiert werden:
Imports System.Web.UI.WebControls
Das Objekt [Label] verfügt über verschiedene Eigenschaften, darunter die Eigenschaft [Text], die den Text darstellt, der vom entsprechenden <asp:label>-Tag angezeigt wird. Hier weisen wir dieser Eigenschaft die aktuelle Uhrzeit zu. Dies geschieht in der Prozedur [Form_Load] des Controllers, die immer ausgeführt wird. In der Prozedur [Form_Init], die ebenfalls immer ausgeführt wird, jedoch vor der Prozedur [Form_Load], speichern wir die Anfrage des Clients in einer Datei namens [request.txt] im Anwendungsordner. Wir werden Gelegenheit haben, diese Datei zu untersuchen, um bestimmte Aspekte der Funktionsweise von Seiten zu verstehen, die Server-Tags verwenden.
Das Objekt [Label] verfügt über zahlreiche Eigenschaften, Methoden und Ereignisse. Der Leser wird gebeten, die Dokumentation zur Klasse [Label] zu lesen, um diese kennenzulernen. Dies wird im weiteren Verlauf immer so gehandhabt. Für jedes Tag stellen wir nur die wenigen Eigenschaften vor, die wir benötigen.
7.3.2. Die Tests
Wir legen die Dateien (form1.aspx, form1.aspx.vb) in einem Ordner <application-path> ab und starten Cassini mit den Parametern (<application-path>,/form1). Anschließend rufen wir die URL [http://localhost/form1/form1.aspx] auf. Wir erhalten folgendes Ergebnis:

Der vom Browser empfangene Code HTML lautet wie folgt:
<HTML>
<HEAD>
<title>Webforms</title>
</HEAD>
<body>
<span id="lblHeure">Il est 19:39:37</span>
</body>
</HTML>
Man sieht, dass das Server-Tag
<asp:Label Runat="server" ID="lblHeure" />
in den folgenden Code HTML umgewandelt wurde:
Es handelt sich um die Eigenschaft [Text] des Objekts [lblHeure], die zwischen den Tags <span> und </span> eingefügt wurde. Die vom Client gestellte und in [request.txt] gespeicherte Anfrage lautet wie folgt:
GET /form1/form1.aspx HTTP/1.1
Cache-Control: max-age=0
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0,1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
Alles ganz normal.
7.3.3. Erstellen der Anwendung mit WebMatrix
Wir haben den Code für die Darstellung der Seite [form1.aspx] von Hand erstellt. Diese Methode ist geeignet, wenn man die Tags kennt. Dann reicht ein einfacher Texteditor aus, um die Benutzeroberfläche zu erstellen. Zu Beginn ist jedoch oft ein grafisches Design-Tool in Verbindung mit einer automatischen Codegenerierung erforderlich, da man die Syntax der benötigten Tags noch nicht kennt. Wir erstellen nun dieselbe Anwendung mit dem Tool WebMatrix. Nach dem Start von WebMatrix wählen wir die Option [File/New File]:

Wir erstellen eine Seite namens ASP.NET. Nachdem wir den vorherigen Assistenten bestätigt haben, erscheint das Entwurfsfenster der Seite [form2.aspx]:


Zur Erinnerung: WebMatrix fasst den Steuerungs- und den Darstellungscode der Seite in einer einzigen Datei zusammen, in diesem Fall [form2.aspx]. Die Registerkarte [All] zeigt den Inhalt dieser Textdatei an. Man kann bereits jetzt feststellen, dass sie nicht leer ist:

Der Nachteil solcher Tools ist, dass sie oft unnötigen Code generieren. Das ist hier der Fall, wo WebMatrix ein Tag HTML <form> generiert hat, obwohl wir kein Formular erstellen werden... Außerdem ist festzustellen, dass das Dokument kein <title>-Tag enthält. Wir beheben diese beiden Probleme umgehend, um die folgende neue Version zu erhalten:

Der sogenannte Controller-Code wird zwischen den Tags <script> und </script> eingefügt, wodurch zumindest eine visuelle Trennung zwischen den beiden Codearten – Steuerung und Darstellung – gewährleistet ist. Wir kehren zur Registerkarte [Design] zurück, um unsere Benutzeroberfläche zu entwerfen. Eine Liste der Komponenten ist in einem Werkzeugfenster links neben dem Entwurfsfenster verfügbar:

Das Werkzeugfenster bietet Zugriff auf zwei Arten von Komponenten:
- die Komponenten [WebControls], die in <asp:>-Tags
- die Komponenten [HTML Elements], die in klassische HTML-Tags übersetzt werden. Den Attributen eines HTML-Tags kann jedoch das Attribut [runat="server"] hinzugefügt werden. In diesem Fall sind das Tag HTML und seine Attribute für den Controller über ein Objekt zugänglich, dessen Eigenschaften denen des Tags HTML entsprechen, das es repräsentiert. Diese Tags wurden zuvor als Server-Tags HTML bezeichnet.
Doppelklicken wir auf die Komponente [Label] in der Steuerelementliste [WebControls]. Auf der Registerkarte [Design] erhalten wir folgendes Ergebnis:

Auf der Registerkarte „[All]“ lautet der Code nun wie folgt:
<%@ Page Language="VB" %>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="Label1" runat="server">Label</asp:Label>
</body>
</html>
Zunächst fällt auf, dass das Tag <script> verloren gegangen ist. Es wurde ein <asp:label>-Tag generiert. Es hat den Namen [Label1] und den Wert [Label]. Kehren wir zur Registerkarte „[Design]“ zurück, um diese beiden Werte zu ändern. Wir klicken dort einmal auf die Komponente „[Label]“, um das Eigenschaftenfenster dieser Komponente unten rechts anzuzeigen:

Der Leser wird gebeten, die Eigenschaften des Objekts [Label] zu überprüfen. Zwei davon sind hier für uns von Interesse:
- Text: Dies ist der Text, den das Label anzeigen soll – wir geben die leere Zeichenfolge ein (c.a.d. nichts)
- ID: Das ist seine Kennung – wir geben lblHeure ein
Die Registerkarte [Design] sieht dann wie folgt aus:

und der Code von [All] lautet nun:
<%@ Page Language="VB" %>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="lblHeure" runat="server"></asp:Label>
</body>
</html>
Der Layout-Teil der Seite ist fertig. Nun müssen wir noch den Steuerungscode schreiben, der die Uhrzeit in die Eigenschaft [Text] von [lblHeure] einfügt. Wir fügen auf der Registerkarte [All] den folgenden Code hinzu:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' speichert die Uhrzeit in lblHeure
lblHeure.Text = "Il est " + Date.Now.ToString("T")
End Sub
</script>
<html>
<head>
<title>webforms</title>
</head>
<body>
<asp:Label id="lblHeure" runat="server"></asp:Label>
</body>
</html>
Es ist zu beachten, dass im Controller-Code das Objekt [lblHeure] nicht wie zuvor deklariert wird:
Protected lblHeure As New System.Web.UI.WebControls.Label
Tatsächlich werden alle <asp:>-Serverkomponenten des Präsentationsteils implizit im Controller-Code deklariert. Eine zusätzliche Deklaration führt daher zu einem Kompilierungsfehler, der darauf hinweist, dass das Objekt bereits deklariert ist. Wir sind bereit für die Ausführung. Wir wählen die Option [View/Start] oder die Verknüpfung [F5]. Cassini wird automatisch mit den folgenden Parametern gestartet:

Wir akzeptieren diese Werte. Der Standardbrowser des Systems wird automatisch gestartet, um die URL [http://localhost/form2.aspx] aufzurufen. Wir erhalten das folgende Ergebnis:

Im weiteren Verlauf werden wir hauptsächlich das Tool WebMatrix verwenden, um die Erstellung und das Testen der kurzen Programme, die wir schreiben werden, zu vereinfachen.
7.4. Die Literal-Komponente
7.4.1. Verwendung
Das Tag <asp:literal> ermöglicht es, dynamischen Text in den Präsentationscode einer Seite einzufügen, ähnlich wie das Tag <asp:label>. Sein Hauptattribut ist [Text], das den Text darstellt, der unverändert in den HTML-Fluss der Seite eingefügt wird. Dieses Tag reicht aus, wenn man nicht beabsichtigt, den Text, den man in den HTML-Fluss einfügen möchte, zu formatieren. Während die Klasse [Label] die Formatierung mithilfe von Attributen wie [BorderColor, BorderWidth, Font, ...] ermöglicht, verfügt die Klasse [Literal] über keines dieser Attribute. Der Leser kann das vorherige Beispiel vollständig übernehmen, indem er die Komponente [Label] durch eine Komponente [Literal] ersetzt.
7.5. Die Button-Komponente
7.5.1. Verwendung
Mit dem Tag <asp:Button> lässt sich eine Schaltfläche vom Typ [Submit] in ein Formular einfügen, die eine Ereignisbehandlung mit sich bringt, die der in Windows-Anwendungen ähnelt. Genau diesen Punkt möchten wir hier näher beleuchten. Wir erstellen die folgende Seite [form3.aspx]:

Diese mit WebMatrix erstellte Seite besteht aus drei Komponenten:
Nr. | Name | Typ | Eigenschaften | Rolle |
1 | Schaltfläche | text=Schaltfläche1 | Absenden-Schaltfläche | |
2 | Button | text=Schaltfläche 2 | Absenden-Schaltfläche | |
3 | Beschriftung | text= | Informationsmeldung |
Der von WebMatrix für diesen Abschnitt generierte Code lautet wie folgt:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form runat="server">
<p>
<asp:Button id="Button1" runat="server" Text="Bouton1"></asp:Button>
<asp:Button id="Button2" runat="server" Text="Bouton2"></asp:Button>
</p>
<p>
<asp:Label id="lblInfo" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Wir finden die Komponenten [Button] und [Label], die bei der grafischen Gestaltung der Seite verwendet wurden, in <asp:>-Tags wieder. Beachten Sie das <form runat="server">-Tag, das automatisch generiert wurde. Es handelt sich um ein Server-Tag HTML, c.a.d. Ein klassisches Tag HTML, das jedoch durch ein vom Controller bearbeitbares Objekt dargestellt wird. Der Code des Tags <form> wird anhand des Werts generiert, den der Controller diesem Objekt zuweist.
Fügen wir im Controller-Teil des Codes die Prozedur [Page_Init] hinzu, die das Ereignis [Init] der Seite verarbeitet. Dort platzieren wir den Code, der die Anfrage des Clients in der Datei [request.txt] speichert. Wir werden dies benötigen, um die Funktionsweise der Schaltflächen zu verstehen.
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' speichert die aktuelle Anfrage in request.txt im Seitenordner
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
</script>
Es ist zu beachten, dass wir die Klausel [Handles MyBase.Init] nicht hinter der Deklaration der Prozedur [Page_Init] eingefügt haben. Tatsächlich verfügt das Ereignis [Init] des Objekts [Page] über einen Standardhandler namens [Page_Init]. Verwendet man diesen Handlernamen, wird die Klausel [Handles Page.Init] überflüssig. Ihre Angabe führt jedoch nicht zu einem Fehler.
7.5.2. Tests
Wir starten die Anwendung unter WebMatrix über [F5]. Wir erhalten die folgende Seite:

Der vom Browser empfangene Code HTML lautet wie folgt:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />
<p>
<input type="submit" name="Button1" value="Bouton1" id="Button1" />
<input type="submit" name="Button2" value="Bouton2" id="Button2" />
</p>
<p>
<span id="lblInfo"></span>
</p>
</form>
</body>
</html>
Beachten Sie bitte Folgendes:
- Das Tag <form runat="server"> wurde in das Tag HTML umgewandelt
Zwei Attribute wurden festgelegt: [method="post"] und [action="form3.aspx"]. Können diesen Attributen unterschiedliche Werte zugewiesen werden? Wir werden versuchen, diesen Punkt etwas später zu klären. An dieser Stelle sei festgehalten, dass das Formular an die URL [form3.aspx] gesendet wird. Zwei weitere Attribute, [name, id], wurden ebenfalls festgelegt. Meistens werden sie ignoriert. Enthält die Seite jedoch browserseitig ausgeführten JavaScript-Code, ist das Attribut [name] des Tags <form> nützlich.
- Die <asp:button>-Tags wurden zu HTML-Tags für [submit]-Schaltflächen. Ein Klick auf eine dieser Schaltflächen löst daher einen „Post“ des Formulars [_ctl10] an die URL [form3.aspx] aus.
- Das Tag <asp:label> wurde zu einem Tag HTML <span>
- Es wurde ein verstecktes Feld [__VIEWSTATE] mit einem seltsamen Wert generiert:
Dieses Feld stellt in verschlüsselter Form den Status des an den Client gesendeten Formulars dar. Dieser Status entspricht der Summe der Werte aller Formularkomponenten. Da [__VIEWSTATE] Teil des Formulars ist, wird sein Wert zusammen mit dem Rest an den Server gesendet. Dadurch kann der Server erkennen, welche Komponente des Formulars ihren Wert geändert hat, und gegebenenfalls Entscheidungen treffen. Diese erfolgen in Form von Ereignissen vom Typ „Das Feld TextBox hat seinen Wert geändert“.
7.5.3. Die Anfragen des Clients
Sobald die Seite [form3.aspx] im Browser angezeigt wird, rufen wir sie erneut auf und sehen uns die Anfrage an, die der Browser gesendet hat, um sie abzurufen. Wir erinnern uns, dass unsere Anwendung diese in der Datei [request.txt] im Anwendungsordner speichert:
GET /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
Das ist ein klassischer GET. Klicken wir nun auf die Schaltfläche [Bouton1] auf der Seite im Browser. Es scheint nichts zu passieren. Dennoch wissen wir, dass das Formular abgeschickt wurde. Das geht aus dem Code HTML der Seite hervor. Dies wird durch den neuen Inhalt von [request.txt] bestätigt:
POST /form3.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 80
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form3.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__VIEWSTATE=dDwxNTY0NjIwMjUwOzs%2B2mcnJczeuvF2PEfvmtv7uiUhWUw%3D&Button1=Bouton1
Der erste Header HTTP zeigt deutlich, dass der Client einen POST an die URL [/form3.aspx] gesendet hat. Die letzte Zeile zeigt die übermittelten Werte:
- den Wert des versteckten Felds __VIEWSTATE
- den Wert der Schaltfläche, auf die geklickt wurde
Wenn wir auf [Bouton2] klicken, sind die vom Browser übermittelten Werte wie folgt:
Der Wert des versteckten Feldes ist immer derselbe, aber es wurde der Wert von [Button2] übermittelt. Der Server kann somit erkennen, welche Schaltfläche verwendet wurde. Er nutzt diese Information, um ein Ereignis auszulösen, das von der Seite verarbeitet werden kann, sobald diese geladen ist.
7.5.4. Das Click-Ereignis eines Button-Objekts verarbeiten
Erinnern wir uns an die Funktionsweise einer .aspx-Seite. Diese ist ein von der Klasse [Page] abgeleitetes Objekt. Nennen wir die abgeleitete Klasse [unePage]. Wenn der Server eine Anfrage für eine solche Seite erhält, wird ein Objekt vom Typ [unePage] durch den Befehl new unePage(...) instanziiert. Anschließend generiert der Server zwei Ereignisse namens [Init] und [Load] in dieser Reihenfolge. Das Objekt [unePage] kann diese verarbeiten, indem es die Ereignisbehandler [Page_Init] und [Page_Load] bereitstellt. Anschließend werden weitere Ereignisse generiert. Wir werden später noch darauf zurückkommen. Wenn die Anfrage des Clients ein POST ist, generiert der Server das Ereignis [Click] der Schaltfläche, die dieses POST ausgelöst hat. Wenn die Klasse [unePage] einen Handler für dieses Ereignis vorgesehen hat, wird dieser aufgerufen. Betrachten wir diesen Mechanismus anhand von WebMatrix. Doppelklicken wir auf der Registerkarte [Design] von [form3.aspx] auf die Schaltfläche [Bouton1]. Wir gelangen dann automatisch in die Registerkarte „[Code]“, in den Hauptteil einer Prozedur namens „[Button1_Click]“. Um dies besser zu verstehen, wechseln wir in die Registerkarte „[All]“ und sehen uns den gesamten Code an. Folgende Änderungen wurden vorgenommen:
<%@ Page Language="VB" %>
<script runat="server">
...
Sub Button1_Click(sender As Object, e As EventArgs)
End Sub
</script>
<html>
...
<body>
<form runat="server">
...
<asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button>
</form>
</body>
</html>
Dem Tag <asp:Button> von [Button1] wurde ein neues Attribut [onclick="Button1_Click"] hinzugefügt. Dieses Attribut gibt die Prozedur an, die das Ereignis [Click] für das Objekt [Button1] verarbeitet, in diesem Fall die Prozedur [Button1_Click]. Nun müssen wir diese nur noch schreiben:
Sub Button1_Click(sender As Object, e As EventArgs)
' Klick auf Schaltfläche 1
lblInfo.Text="Vous avez cliqué sur [Bouton1]"
End Sub
Die Prozedur fügt in das Label [lblInfo] eine Informationsmeldung ein. Wir verfahren auf die gleiche Weise mit der Schaltfläche [Bouton2], um die folgende neue Seite [form3.aspx] zu erhalten:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' speichert die aktuelle Anfrage unter request.txt im Seitenordner
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Sub Button1_Click(sender As Object, e As EventArgs)
' Klick auf Schaltfläche 1
lblInfo.Text="Vous avez cliqué sur [Bouton1]"
End Sub
Sub Button2_Click(sender As Object, e As EventArgs)
' Klick auf Schaltfläche 2
lblInfo.Text="Vous avez cliqué sur [Bouton2]"
End Sub
</script>
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form runat="server">
<p>
<asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button>
<asp:Button id="Button2" onclick="Button2_Click" runat="server" Text="Bouton2" BorderStyle="None"></asp:Button>
</p>
<p>
<asp:Label id="lblInfo" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Wir starten den Lauf mit [F5], um die folgende Seite zu erhalten:

Wenn wir uns den erhaltenen Code HTML ansehen, stellen wir fest, dass er sich gegenüber dem der vorherigen Version der Seite nicht verändert hat:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" />
<p>
<input type="submit" name="Button1" value="Bouton1" id="Button1" />
<input type="submit" name="Button2" value="Bouton2" id="Button2" />
</p>
<p>
<span id="lblInfo"></span>
</p>
</form>
</body>
</html>
Wenn wir auf [Bouton1] klicken, erhalten wir folgende Antwort:

Der für diese Antwort erhaltene Code HTML lautet wie folgt:
<html>
<head>
<title>asp:button</title>
</head>
<body>
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDU+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPFZvdXMgYXZleiBjbGlxdcOpIHN1ciBbQm91dG9uMV07Pj47Pjs7Pjs+Pjs+Pjs+4oO98Vd244kj0lPMXReWOwJ1WW0=" />
<p>
<input type="submit" name="Button1" value="Bouton1" id="Button1" />
<input type="submit" name="Button2" value="Bouton2" id="Button2" />
</p>
<p>
<span id="lblInfo">Vous avez cliqué sur [Bouton1]</span>
</p>
</form>
</body>
</html>
Wir können feststellen, dass sich der Wert des versteckten Felds [__VIEWSTATE] geändert hat. Dies spiegelt die Wertänderung der Komponente [lblInfo] wider.
7.5.5. Ereignisse im Lebenszyklus einer Anwendung ASP.NET
Die Dokumentation ASP.NET enthält eine Liste der Ereignisse, die vom Server im Laufe des Lebenszyklus einer Anwendung generiert werden:
- Bei der allerersten Anfrage, die die Anwendung erhält, wird das Ereignis [Start] für das Objekt [HttpApplication] der Anwendung generiert. Dieses Ereignis kann durch die Prozedur [Application_Start] in der Datei [global.asax] der Anwendung verarbeitet werden.
Anschließend folgt eine Reihe von Ereignissen, die sich für jede empfangene Anfrage wiederholen:
- Wenn die Anfrage kein Sitzungstoken gesendet hat, wird eine neue Sitzung gestartet und ein Ereignis [Start] für das der Anfrage zugeordnete Objekt [Session] generiert. Dieses Ereignis kann durch die Prozedur [Session_Start] in der Datei [global.asax] der Anwendung verarbeitet werden.
- Der Server generiert das Ereignis [BeginRequest] für das Objekt [HttpApplication]. Es kann durch die Prozedur [Application_BeginRequest] in der Datei [global.asax] der Anwendung verarbeitet werden.
- Der Server lädt die durch die Anfrage angeforderte Seite. Er instanziiert ein Objekt [Page] und generiert anschließend zwei Ereignisse für dieses Objekt: [Init] und [Load]. Diese beiden Ereignisse können durch die Prozeduren [Page_Init] und [Page_Load] der Seite verarbeitet werden.
- Anhand der empfangenen übermittelten Werte generiert der Server weitere Ereignisse: [TextChanged] für eine Komponente [TextBox], deren Wert sich geändert hat, [CheckedChanged] für ein Optionsfeld, dessen Wert sich geändert hat, [SelectedIndexChanged] für eine Liste, deren ausgewähltes Element sich geändert hat, … Wir werden im weiteren Verlauf auf die wichtigsten Ereignisse für jede der Serverkomponenten eingehen, die wir vorstellen werden. Jedes Ereignis E an einem Objekt namens O kann durch eine Prozedur mit dem Namen O_E abgehandelt werden.
- Die Reihenfolge der Verarbeitung der oben genannten Ereignisse ist nicht garantiert. Daher dürfen die Handler keine Annahmen über diese Reihenfolge treffen. Es ist jedoch sichergestellt, dass das Ereignis [Click] der Schaltfläche, das das Ereignis POST ausgelöst hat, als letztes verarbeitet wird.
- Sobald die Seite fertig ist, sendet der Server sie an den Client. Zuvor generiert er das Ereignis [PreRender], das von der Prozedur [Page_PreRender] der Seite verarbeitet werden kann.
- Sobald die Antwort HTML an den Client gesendet wurde, wird die Seite aus dem Speicher entladen. Dabei werden zwei Ereignisse generiert: [Unload] und [Disposed]. Die Seite kann diese Ereignisse nutzen, um Ressourcen freizugeben.
Die Anwendung kann auch Ereignisse außerhalb einer Client-Anfrage empfangen:
- Das Ereignis [End] für ein Objekt [Session] der Anwendung tritt auf, wenn eine Sitzung beendet wird. Dies kann auf ausdrückliche Anforderung des Seitencodes geschehen oder weil die der Sitzung zugewiesene Lebensdauer überschritten wurde. Die Prozedur [Session_End] in der Datei [global.asax] verarbeitet dieses Ereignis. Dort werden in der Regel Ressourcen freigegeben, die in [Session_Start] abgerufen wurden.
- Das Ereignis [End] am Objekt [HttpApplication] der Anwendung tritt auf, wenn die Anwendung beendet wird. Dies geschieht insbesondere beim Herunterfahren des Web-Servers. Die Prozedur [Application_End] aus der Datei [global.asax] verarbeitet dieses Ereignis. Dort werden in der Regel Ressourcen freigegeben, die in [Application_Start] bereitgestellt wurden.
Folgende Punkte sind zu beachten:
- Das bisherige Ereignismodell basiert auf klassischen Client-Server-Austauschen (HTTP). Dies wird deutlich, wenn man die ausgetauschten Header (HTTP) betrachtet.
- Die Verarbeitung der bisherigen Ereignisse erfolgt immer serverseitig. Ein Klick auf eine Schaltfläche kann natürlich durch ein serverseitiges JavaScript-Skript verarbeitet werden. Dabei handelt es sich jedoch nicht um ein Serverereignis, und wir befinden uns hier in einem von ASP.NET unabhängigen Technologiebereich.
Zu welchem Zeitpunkt werden Ereignisse verarbeitet, unabhängig davon, ob sie serverseitig (Ereignisse im Zusammenhang mit Serverkomponenten) oder im Browser durch JavaScript-Skripte verarbeitet werden?
Nehmen wir das Beispiel einer Dropdown-Liste. Wenn der Benutzer das darin ausgewählte Element ändert, kann das Ereignis (Änderung des ausgewählten Elements) verarbeitet werden oder auch nicht, und falls es verarbeitet wird, kann dies zu unterschiedlichen Zeitpunkten geschehen.
- Wenn man es sofort verarbeiten möchte, gibt es zwei Möglichkeiten:
- Es kann vom Browser mithilfe eines JavaScript-Skripts verarbeitet werden. Der Server greift dabei nicht ein. Damit dies möglich ist, muss die Seite mit den auf der Seite vorhandenen Werten neu aufgebaut werden können.
- Es kann vom Server verarbeitet werden. Hierfür gibt es nur eine Lösung: Das Formular muss zur Verarbeitung an den Server gesendet werden. Es handelt sich also um die Operation [submit]. Wir werden sehen, dass in diesem Fall eine Serverkomponente namens [DropDownList] verwendet wird und ihr Attribut [AutoPostBack] auf [true] gesetzt wird. Das bedeutet, dass bei einer Änderung des in der Dropdown-Liste ausgewählten Elements das Formular sofort an den Server gesendet werden muss. Der Server generiert in diesem Fall für das Objekt [DropDownList] einen Code HTML, der mit einer JavaScript-Funktion verknüpft ist, die dafür zuständig ist, einen [submit] zu erstellen, sobald das Ereignis „Änderung des ausgewählten Elements“ eintritt. Dieser [submit] sendet das Formular an den Server, wobei darin versteckte Felder gesetzt werden, um anzuzeigen, dass der [post] auf eine Änderung der Auswahl in der Dropdown-Liste zurückzuführen ist. Der Server generiert daraufhin das Ereignis [SelectedIndexChanged], das von der Seite verarbeitet werden kann.
- Wenn man es verarbeiten möchte, aber nicht sofort, setzt man das Attribut [AutoPostBack] der Serverkomponente [DropDownList] auf [false]. In diesem Fall generiert der Server für das Objekt [DropDownList] den klassischen Code HTML einer <select>-Liste ohne zugehörige JavaScript-Funktion. Es geschieht also nichts, wenn der Benutzer die Auswahl in der Dropdown-Liste ändert. Wenn der Benutzer das Formular jedoch beispielsweise über eine Schaltfläche mit dem Code [submit] absendet, kann der Server erkennen, dass sich die Auswahl geändert hat. Wir haben nämlich gesehen, dass das an den Server gesendete Formular ein verstecktes Feld mit dem Code [__VIEWSTATE] enthält, das den Status aller Elemente des gesendeten Formulars in verschlüsselter Form darstellt. Wenn der Server das vom Client gesendete neue Formular empfängt, kann er überprüfen, ob sich das ausgewählte Element in der Dropdown-Liste geändert hat oder nicht. Ist dies der Fall, generiert er das Ereignis [SelectedIndexChanged], das die Seite dann verarbeiten kann. Um diesen Mechanismus vom vorherigen zu unterscheiden, sagen manche Autoren, dass das Ereignis „Änderung der Auswahl“ im Browser „zwischengespeichert“ wurde, als es dort auftrat. Es wird vom Server erst dann verarbeitet, wenn der Browser das Formular an ihn übermittelt, was oft nach einem Klick auf eine Schaltfläche mit dem Namen [submit] geschieht.
- Wenn man das Ereignis schließlich nicht verarbeiten möchte, setzt man das Attribut [AutoPostBack] der Serverkomponente [DropDownList] auf [false] und schreibt den Handler für das entsprechende Ereignis [SelectedIndexChanged] nicht.
Sobald der Entwickler den Mechanismus der Ereignisverarbeitung verstanden hat, wird er eine Webanwendung nicht mehr wie eine Windows-Anwendung konzipieren. Denn während eine Änderung der Auswahl in einer Combobox einer Windows-Anwendung dazu genutzt werden kann, das Erscheinungsbild des Formulars, in dem sie sich befindet, sofort zu ändern, wird man in einer Webanwendung eher zögern, dieses Ereignis sofort zu verarbeiten, wenn es einen „Post“-Vorgang des Formulars an den Server und damit einen Netzwerk-Hin- und Rückweg zwischen Client und Server erfordert. Aus diesem Grund ist die Eigenschaft [AutoPostBack] der Serverkomponenten standardmäßig auf [false] gesetzt. Außerdem kann der Mechanismus [AutoPostBack], der auf JavaScript-Skripten basiert, die vom Webserver automatisch in das an den Client gesendete Formular generiert werden, nur verwendet werden, wenn sicher ist, dass der Client-Browser die Ausführung von JavaScript-Skripten in seinem Browser zugelassen hat. Formulare werden daher häufig wie folgt aufgebaut:
- Die Serverkomponenten des Formulars haben die Eigenschaft [AutoPostBack] bis [false]
- Das Formular verfügt über eine oder mehrere Schaltflächen, die für die Ausführung des [POST] des Formulars zuständig sind
- Im Controller-Code der Seite werden die Handler nur für die Ereignisse geschrieben, die man behandeln möchte, meist das Ereignis [Click] für eine der Schaltflächen.
7.6. Die Komponente TextBox
7.6.1. Verwendung
Mit dem Tag <asp:TextBox> lässt sich ein Eingabefeld in den Layout-Code einer Seite einfügen. Wir erstellen eine Seite [form4.aspx], um das folgende Layout zu erhalten:

4321Diese mit WebMatrix erstellte Seite enthält folgende Komponenten:
Nr. | Name | Typ | Eigenschaften | Rolle |
1 | TextBox | AutoPostback=true Text= | Eingabefeld | |
2 | TextBox | AutoPostback=false Text= | Eingabefeld | |
3 | Bezeichnung | text= | Informationsmeldung zum Inhalt von [TextBox1] | |
3 | Bezeichnung | text= | Informationsmeldung zum Inhalt von [TextBox2] |
Der von WebMatrix für diesen Abschnitt generierte Code lautet wie folgt:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form runat="server">
<p>
Texte 1 :
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True"></asp:TextBox>
</p>
<p>
Texte 2 :
<asp:TextBox id="TextBox2" runat="server"></asp:TextBox>
</p>
<p>
<asp:Label id="lblInfo1" runat="server"></asp:Label>
</p>
<p>
<asp:Label id="lblInfo2" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Doppelklicken Sie auf der Registerkarte „[Design]“ auf die Komponente „[TextBox1]“. Das Gerüst des Ereignismanagers [TextChanged] für dieses Objekt wird daraufhin generiert (Registerkarte [All]):
<%@ Page Language="VB" %>
<script runat="server">
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
End Sub
</script>
<html>
...
<body>
...
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
</p>
....
</form>
</body>
</html>
Das Attribut [OnTextChanged="TextBox1_TextChanged"] wurde dem Tag <asp:TextBox id="TextBox1"> hinzugefügt, um den Ereignis-Handler [TextChanged] für [TextBox1] festzulegen. Nun schreiben wir die Prozedur [TextBox1_Changed].
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
' Textänderung
lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox1]. Texte 1=["+textbox1.Text+"]"
End Sub
In der Prozedur schreiben wir in das Label [lblInfo1] eine Meldung, die das Ereignis signalisiert und den Inhalt von [TextBox1] angibt. Das Gleiche tun wir für [TextBox2]. Wir geben außerdem die Uhrzeit an, um die Bearbeitung der Ereignisse besser nachverfolgen zu können. Der endgültige Code für [form4.aspx] lautet wie folgt:
<%@ Page Language="VB" %>
<script runat="server">
Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
' speichert die aktuelle Anfrage unter request.txt im Seitenordner
Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"
Me.Request.SaveAs(requestFileName, True)
End Sub
Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
' Textänderung
lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox1]. Texte 1=["+textbox1.Text+"]"
End Sub
Sub TextBox2_TextChanged(sender As Object, e As EventArgs)
' Textänderung
lblInfo2.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox2]. Texte 2=["+textbox2.Text+"]"
End Sub
</script>
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form runat="server">
<p>
Texte 1 :
<asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox>
</p>
<p>
Texte 2 :
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
</p>
<p>
<asp:Label id="lblInfo1" runat="server"></asp:Label>
</p>
<p>
<asp:Label id="lblInfo2" runat="server"></asp:Label>
</p>
</form>
</body>
</html>
Wir haben die Prozedur [Page_Init] hinzugefügt, um die Anfrage des Kunden wie im vorherigen Beispiel zu speichern.
7.6.2. Tests
Wir starten die Anwendung unter WebMatrix über [F5]. Wir erhalten die folgende Seite:

Der vom Browser empfangene Code HTML lautet wie folgt:
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
</form>
</body>
</html>
In diesem Code gibt es viele Elemente, die automatisch vom Server generiert wurden. Beachten Sie folgende Punkte:
- Es gibt drei versteckte Felder: [__VIEWSTATE], das wir bereits kennengelernt haben, [__EventTarget] und [__EventArgument]. Die beiden letztgenannten Felder dienen zur Verarbeitung des Browser-Ereignisses „change“ im Eingabefeld [TextBox1]
- Aus den Server-Tags <asp:textbox> sind die Tags HTML <input type="text" ...> entstanden, die den Eingabefeldern entsprechen
- Das Server-Tag <asp:textbox id="TextBox1" AutoPostBack="true" ...> hat ein <input type="text" ...>-Tag mit dem Attribut [onchange="__doPostBack('TextBox1','')"] erzeugt. Dieses Attribut besagt, dass bei einer Änderung des Inhalts von [TextBox1] die JavaScript-Funktion [_doPostBack(...)] ausgeführt werden muss. Diese lautet wie folgt:
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
Was bewirkt die obige Funktion? Sie weist jedem der beiden versteckten Felder [__EventTarget] und [__EventArgument] einen Wert zu und sendet anschließend das Formular ab. Dieses wird somit an den Server gesendet. Dies ist die Wirkung von [AutoPostBack]. Das Browser-Ereignis „change“ löst die Ausführung des Codes „__doPostBack('TextBox1','')“ aus. Daraus lässt sich ableiten, dass im übermittelten Formular das versteckte Feld [__EventTarget] den Wert „TextBox1“ und das versteckte Feld [__EventArgument] den Wert „“ haben wird. Dadurch kann der Server erkennen, welche Komponente den Fehler POST verursacht hat.
- Das Server-Tag <asp:textbox id="TextBox2"...> hat ein klassisches <input type="text" ...>-Tag erzeugt, da sein Attribut [AutoPostBack] nicht auf [true] gesetzt war.
- Das Tag <form> gibt an, dass das Formular an [form4.aspx] gesendet wird:
Führen wir unseren ersten Test durch. Geben wir einen Text in das erste Eingabefeld ein:

und setzen wir den Cursor dann in das zweite Eingabefeld. Sofort wird eine neue Seite angezeigt:

Was ist passiert? Als der Cursor das erste Eingabefeld verlassen hat, hat der Browser überprüft, ob sich dessen Inhalt geändert hat. Das war der Fall. Daher hat er auf der Browserseite das Ereignis [change] für das Feld HTML [TextBox1] ausgelöst. Wir haben dann gesehen, dass eine JavaScript-Funktion ausgeführt wurde und das Formular an [form4.aspx] übermittelte. Diese Seite wurde daraufhin vom Server neu geladen. Anhand der vom Formular übermittelten Werte konnte der Server wiederum feststellen, dass sich der Inhalt des Server-Tags [TextBox1] geändert hatte. Die Prozedur [TextBox1_Changed] wurde daher serverseitig ausgeführt. Sie hat eine Meldung in das Label [lblInfo1] eingefügt. Nach Abschluss dieser Prozedur wurde [form4.aspx] an den Browser gesendet. Deshalb befindet sich nun ein Text in [lblInfo1]. Allerdings mag es verwundern, dass sich etwas im Eingabefeld [TextBox1] befindet. Tatsächlich weist kein serverseitig ausgeführter Vorgang diesem Feld einen Wert zu. Hier handelt es sich um einen allgemeinen Mechanismus von Webformularen ASP.NET: Der Server sendet das Formular in dem Zustand zurück, in dem er es empfangen hat. Dazu weist er den Komponenten den Wert wieder zu, der vom Client für sie übermittelt wurde. Für bestimmte Komponenten übermittelt der Client keine Werte. Dies gilt beispielsweise für <asp:label>-Komponenten, die in die Tags <span> übersetzt werden. Wie bereits erwähnt, verfügt das Formular über ein verstecktes Feld, das den Zustand des Formulars zum Zeitpunkt der Übermittlung an den Client repräsentiert. Dieser Status ist die Summe der Status aller Formularkomponenten, einschließlich der <asp:label>-Komponenten, sofern vorhanden. Da das versteckte Feld [__VIEWSTATE] vom Client-Browser gesendet wird, kann der Server den vorherigen Status aller Formularkomponenten wiederherstellen. Nun muss er nur noch diejenigen Komponenten anpassen, deren Wert durch POST geändert wurde.
Sehen wir uns nun in [request.txt] die vom Browser gesendete Anfrage an:
POST /form4.aspx HTTP/1.1
Connection: keep-alive
Keep-Alive: 300
Content-Length: 137
Content-Type: application/x-www-form-urlencoded
Accept: application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0,1
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip,deflate
Accept-Language: en-us,en;q=0.5
Host: localhost
Referer: http://localhost/form4.aspx
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=premier+texte&TextBox2=
Man sieht deutlich den Code „POST“ sowie die übermittelten Parameter. Kehren wir zu unserem Browser zurück und geben wir einen Text in das zweite Eingabefeld ein:

Kehren wir zum Eingabefeld Nr. 1 zurück, um einen neuen Text einzugeben: Diesmal passiert nichts. Warum? Weil das Eingabefeld [TextBox2] nicht die Eigenschaft [AutoPostBack] bei [true] aufweist und das dafür generierte <input type="text"...> nicht das Ereignis [Change], wie sein Code HTML zeigt:
Es tritt also kein Ereignis auf, wenn man das Eingabefeld Nr. 2 verlässt. Geben wir nun einen neuen Text in Feld Nr. 1 ein:

Verlassen wir das Eingabefeld Nr. 1. Sofort wird das Ereignis [Change] für dieses Feld erkannt und das Formular an den Server gesendet, der daraufhin die folgende Seite zurückgibt:

Was ist passiert? Zunächst hat der Browser das Formular gesendet. Dies geht aus der in [request.txt] gespeicherten Client-Anfrage hervor:
POST /form4.aspx HTTP/1.1
....
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjtpPDU%2BOz47bDx0PHA8cDxsPFRleHQ7PjtsPHByZW1pZXIgdGV4dGU7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPDE4OjQyOjI5OiBldnQgW1RleHRDaGFuZ2VkXSBzdXIgW1RleHRCb3gxXS4gVGV4dGUgMT1bcHJlbWllciB0ZXh0ZV07Pj47Pjs7Pjs%2BPjs%2BPjs%2BxLOermpUUUz5rTAa%2FFsjda6lVmo%3D&TextBox1=troisi%C3%A8me+texte&TextBox2=second+texte
Der Server stellt zunächst die vorherigen Werte der Komponenten wieder her, und zwar mithilfe des versteckten Felds [__VIEWSTATE], das der Client an ihn gesendet hat. Mithilfe der übermittelten Felder [TextBox1] und [TextBox2] weist er den Komponenten [TextBox1] und [TextBox2] die Werte zu, die ihm übermittelt wurden. Durch diesen Mechanismus erhält der Client das Formular so zurück, wie es übermittelt wurde. Anschließend erkennt der Server – wiederum anhand der übermittelten Felder [__VIEWSTATE], [TextBox1] und [TextBox2], dass sich die Werte der Eingabefelder [TextBox1] und [TextBox2] geändert haben. Daher generiert er für diese beiden Objekte die Ereignisse [TextChanged]. Die Prozeduren [TextBox1_TextChanged] und [TextBox2_TextChanged] werden ausgeführt, und die Labels [labelInfo1] und [labelInfo2] erhalten einen neuen Wert. Anschließend wird die so geänderte Seite [form4.aspx] an den Client zurückgesendet.
Nun ändern wir erneut das Eingabefeld Nr. 1:

Wenn wir den Eingabecursor aus Feld 1 entfernen, tritt im Browser das Ereignis [Change] ein. Anschließend läuft die bereits erläuterte Abfolge von Ereignissen ab (Post vom Browser zum Server, …, Senden der Serverantwort). Wir erhalten folgende Antwort:

Anhand der für jede Meldung angezeigten Uhrzeit lässt sich erkennen, dass auf dem Server lediglich die Prozedur [TextBox1_Changed] ausgeführt wurde. Die Prozedur [TextBox2_TextChanged] wurde nicht ausgeführt, da sich der Wert von [TextBox2] nicht geändert hat. Geben wir schließlich einen neuen Text in Feld Nr. 2 ein:

Dann setzen wir den Cursor auf Feld Nr. 1 und anschließend wieder auf Feld Nr. 2. Die Seite ändert sich nicht. Warum? Weil wir den Wert von Feld Nr. 1 nicht ändern, tritt das Browser-Ereignis [Change] nicht ein, wenn wir dieses Feld verlassen. Daher wird das Formular nicht an den Server gesendet. Folglich ändert sich nichts auf der Seite. Die Tatsache, dass sich der Inhalt von [lblInfo2] nicht ändert, zeigt uns, dass es kein POST gibt. Gäbe es einen, würde der Server erkennen, dass sich der Inhalt von [TextBox2] geändert hat, und müsste dies in [lblInfo2] widerspiegeln.
Aus diesem Beispiel lässt sich ableiten, dass es keinen Sinn macht, die Eigenschaft [AutoPostBack] von einem [TextBox] auf [true] zu übertragen. Dies führt in den meisten Fällen zu unnötigem Hin- und Her-Verkehr zwischen Client und Server.
7.6.3. Die Rolle des Feldes __VIEWSTATE
Wir haben gesehen, dass der Server systematisch ein verstecktes Feld namens __VIEWSTATE in das von ihm generierte Formular einfügt. Wir haben erklärt, dass dieses Feld den Status des Formulars darstellt und dass der Server, wenn man ihm dieses versteckte Feld zurücksendet, in der Lage ist, den vorherigen Wert des Formulars zu rekonstruieren. Der Status eines Formulars ist die Summe der Status seiner Komponenten. Jede Komponente verfügt über eine Eigenschaft [EnableViewState] mit einem booleschen Wert, der angibt, ob der Status der Komponente in das versteckte Feld [__VIEWSATE] aufgenommen werden soll oder nicht. Standardmäßig hat diese Eigenschaft den Wert [true], wodurch der Status aller Komponenten eines Formulars in [__VIEWSTATE] gespeichert wird. Manchmal ist dies jedoch nicht erwünscht.
Lassen Sie uns einige Versuche durchführen, um die Rolle der Eigenschaft [EnableViewState] besser zu verstehen. Setzen wir diese Eigenschaft für beide Eingabefelder auf [false]:
...
<asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True" EnableViewState="False"></asp:TextBox>
...
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged" EnableViewState="False"></asp:TextBox>
...
Starten wir nun die Anwendung und geben wir einen ersten Text in Feld Nr. 1 ein, bevor wir zu Feld Nr. 2 wechseln. Daraufhin wird ein POST an den Server gesendet und wir erhalten folgende Antwort:

Geben wir einen Text in Feld Nr. 2 ein, ändern wir den Text in Feld Nr. 1 und kehren wir dann zu Feld Nr. 2 zurück (in dieser Reihenfolge). Ein neuer Aufruf von POST wird an den Server gesendet, und wir erhalten folgende Antwort:

Bislang ist alles wie zuvor. Ändern wir nun den Inhalt von Feld Nr. 1 und wechseln dann zu Feld Nr. 2. Es wird ein neuer POST-Aufruf an den Server gesendet. Die Antwort des Servers lautet wie folgt:

Diesmal gibt es eine Änderung. Der Server hat ein Ereignis [TextChanged] in Feld Nr. 2 erkannt, da die Uhrzeit von [lblInfo2] geändert wurde. Es gab jedoch keine Änderung. Dies lässt sich durch die Eigenschaft [EnableViewState=false] von [TextBox2] erklären. Sie bewirkt, dass der Server den vorherigen Status von [TextBox2] nicht in das Feld [__VIEWSTATE] des Formulars eingetragen hat. Dies läuft darauf hinaus, dass ihm die leere Zeichenkette als vorheriger Status zugewiesen wird. Als das Ereignis POST aufgrund der Änderung des Inhalts von [TextBox1] auftrat, verglich der Server den aktuellen Wert von [TextBox2], der [deux] war, mit seinem vorherigen Wert (der leeren Zeichenkette). Daraus schloss er, dass sich der Wert von [TextBox2] geändert hatte, und generierte das Ereignis [TextChanged] für [TextBox2]. Man kann diese Funktionsweise überprüfen, indem man die leere Zeichenkette in [TextBox2] einfügt. Nach der soeben gegebenen Erklärung sollte der Server das Ereignis [TextChanged] für [TextBox2] nicht generieren. Versuchen wir es:

Genau das ist passiert. Die Uhrzeit und der Inhalt von [lblInfo2] zeigen, dass die Prozedur [TextBox2_TextChanged] nicht ausgeführt wurde. Vor diesem Hintergrund betrachten wir nun die Eigenschaft [EnableViewState] der vier Formularkomponenten:
Wir möchten den Status dieser Komponente beibehalten, damit der Server weiß, ob sich dieser geändert hat oder nicht | |
ebenso | |
Der Status dieser Komponente soll nicht beibehalten werden. Der Text soll bei jedem neuen POST neu berechnet werden. Wird er nicht neu berechnet, muss er leer sein. All dies wird mit [EnableViewState=false] erreicht | |
ebenso |
Unsere Übersichtsseite sieht nun wie folgt aus:
...
<asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True"></asp:TextBox>
...
<asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox>
....
<asp:Label id="lblInfo1" runat="server" enableviewstate="False"></asp:Label>
...
<asp:Label id="lblInfo2" runat="server" enableviewstate="False"></asp:Label>
...
Führen wir dieselbe Testreihe wie zuvor durch. An der Stelle, an der wir den Bildschirm erhalten haben
version 1

erhalten wir nun:
version 2

In diesem Schritt haben wir den Inhalt von Feld 1 geändert, ohne den von Feld 2 zu ändern, wodurch die Prozedur [TextBox2_TextChanged] serverseitig nicht ausgeführt wurde, was wiederum bedeutete, dass das Feld [lblInfo2] keinen neuen Wert erhielt. Es wird daher mit seinem vorherigen Wert angezeigt. In Version 1 ([EnableViewState=true]) war dieser vorherige Wert der eingegebene Wert. In Version 2 ([EnableViewState=false]) ist dieser vorherige Wert die leere Zeichenkette.
Manchmal ist es nicht notwendig, den vorherigen Zustand der Komponenten beizubehalten. Anstatt für jede einzelne Komponente [EnableViewState=false] anzugeben, kann man festlegen, dass die Seite ihren Zustand nicht beibehalten soll. Dies geschieht in der Anweisung [Page] im Präsentationscode:
In diesem Fall wird der Zustand einer Komponente – unabhängig vom Wert ihrer Eigenschaft [EnableViewState] – nicht in das versteckte Feld [__VIEWSTATE] geschrieben. Es verhält sich dann so, als wäre ihr vorheriger Zustand die leere Zeichenkette.
Verwenden wir nun den Client [curl], um weitere Mechanismen zu verdeutlichen. Wir rufen zunächst die URL [http://localhost/form4.aspx] auf:
dos>curl --include --url http://localhost/form4.aspx
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Sun, 04 Apr 2004 17:51:14 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 1077
Connection: Close
<html>
<head>
<title>asp:textbox</title>
</head>
<body>
<form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" />
<script language="javascript">
<!--
function __doPostBack(eventTarget, eventArgument) {
var theform = document._ctl0;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
// -->
</script>
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
</form>
</body>
</html>
Wir erhalten vom Server den Code HTML aus [form4.aspx]. Er unterscheidet sich nicht von dem Code, den der Browser erhalten hatte. Zur Erinnerung: Dies ist die Anfrage, die der Browser beim Absenden des Formulars gestellt hat:
POST /form4.aspx HTTP/1.1
Connection: keep-alive
...
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316
__EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&TextBox1=premier+texte&TextBox2=
Machen wir dasselbe mit POST, aber ohne das Feld [__VIEWSTATE] zu senden:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=erster+Text --data TextBox2=
...................
<p>
Texte 1 :
<input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1">19:57:48: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
..............
Folgende Punkte sind zu beachten:
- Der Server hat ein Ereignis [TextChanged] auf [TextBox1] erkannt, da er den Text [lblInfo1] generiert hat. Das Fehlen von [__VIEWSTATE] hat ihn nicht beeinträchtigt. In dessen Abwesenheit geht er davon aus, dass der vorherige Wert eines Eingabefeldes die leere Zeichenkette ist.
- Es gelang ihm, den für [TextBox1] übermittelten Text in das Attribut [value] des Tags [TextBox1] einzufügen, sodass das Feld [TextBox1] wieder mit dem eingegebenen Wert angezeigt wurde. Dazu benötigt er nicht [__VIEWSTATE], sondern lediglich den für [TextBox1] übermittelten Wert.
Führen wir nun dieselbe Abfrage erneut durch, ohne etwas zu ändern. Wir erhalten die folgende neue Antwort:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=erster+Text --data TextBox2=
<p>
Texte 1 :
<input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1">20:05:47: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
Da [__VIEWSTATE] fehlte, konnte der Server nicht erkennen, dass sich der Wert des Feldes [TextBox1] nicht geändert hatte. Er verhält sich daher so, als wäre der vorherige Wert die leere Zeichenkette. Daher hat er hier das Ereignis [TextChanged] für [TextBox1] generiert. Führen wir dieselbe Abfrage noch einmal aus, diesmal mit dem leeren Feld [TextBox1] und dem nicht leeren Feld [TextBox2]:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox2=zweiter+Text --data TextBox1=
......
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" value="second texte" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2">20:11:54: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span>
</p>
......
Da [__VIEWSTATE] nicht vorhanden war, wurde der vorherige Wert von [TextBox1] als leere Zeichenfolge betrachtet. Da der gebuchte Wert von [TextBox1] ebenfalls die leere Zeichenkette war, wurde das Ereignis [TextChanged] zu [TextBox1] nicht generiert. Die Prozedur [TextBox1_TextChanged] wurde nicht ausgeführt, sodass das Feld [lblInfo1] keinen neuen Wert erhalten hat. Es ist bekannt, dass die Komponente in diesem Fall ihren alten Wert beibehält. Hier ist dies jedoch nicht der Fall: [lblInfo1] hat seinen vorherigen Wert verloren. Dies liegt daran, dass dieser Wert in [__VIEWSTATE] gesucht wird. Da dieses Feld fehlt, wurde [lblInfo1] die leere Zeichenfolge zugewiesen. Bei [TextBox2] hat der Server den geposteten Wert [second texte] mit dem vorherigen Wert verglichen. Da [__VIEWSTATE] fehlt, entspricht dieser vorherige Wert der leeren Zeichenkette. Da sich der übermittelte Wert von [TextBox2] von der leeren Zeichenkette unterschied, wurde das Ereignis [TextChanged] für [TextBox2] generiert. Die Prozedur [TextBox2_TextChanged] wurde ausgeführt, und das Feld [lblInfo2] erhielt einen neuen Wert.
Man kann sich fragen, ob die Parameter [__EVENTTARGET] und [__EVENTARGUMENT] tatsächlich nützlich sind. Werden diese Parameter nicht gesendet, weiß der Server nicht, durch welches Ereignis das Ereignis [submit] ausgelöst wurde. Versuchen wir es:
dos>curl --include --url http://localhost/form4.aspx --data TextBox2=zweiter+Text --data TextBox1=erster+Text
..............................
<p>
Texte 1 :
<input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" id="TextBox2" />
</p>
<p>
<span id="lblInfo1"></span>
</p>
<p>
<span id="lblInfo2"></span>
</p>
</form>
.....................
Es ist ersichtlich, dass kein Ereignis [TextChanged] verarbeitet wurde. Außerdem werden die Werte der übermittelten Felder [TextBox1] und [TextBox2] nicht wiederhergestellt. Es verhält sich tatsächlich so, als hätte man einen GET ausgeführt. Alles kehrt zum Normalzustand zurück, wenn das Feld [__EVENTTARGET] in den übermittelten Feldern enthalten ist, auch wenn es keinen Wert hat:
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET= --data TextBox2=zweiter+Text --data TextBox1=erster+Text
.......
<p>
Texte 1 :
<input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />
</p>
<p>
Texte 2 :
<input name="TextBox2" type="text" value="second texte" id="TextBox2" />
</p>
<p>
<span id="lblInfo1">20:34:14: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span>
</p>
<p>
<span id="lblInfo2">20:34:14: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span>
</p>
........
7.6.4. Weitere Eigenschaften der Komponente TextBox
Die Serverkomponente [TextBox] ermöglicht zudem die Generierung der Tags HTML <input type="password"..> und <textarea>..</textarea>, c.a.d. Diese Tags entsprechen jeweils einem geschützten Eingabefeld und einem mehrzeiligen Eingabefeld. Diese Generierung wird durch die Eigenschaft [TextMode] der Komponente [TextBox] gesteuert. Sie kann drei Werte annehmen:
Wert | Generiertes Tag HTML |
<input type="text" ...> | |
<textarea>...</textarea> | |
<input type="password ...> |
Wir untersuchen die Verwendung dieser Eigenschaften anhand des folgenden Beispiels [form5.aspx]:

Nr. | Name | Typ | Eigenschaften | Rolle |
1 | Schaltfläche | Schaltfläche [submit] – dient dazu, den Inhalt von [TextBox1] zu dem von [TextBox2] hinzuzufügen | ||
2 | TextBox | TextMode=Password Text= | geschütztes Eingabefeld | |
3 | TextBox | TextMode=Mehrzeilig Text= | fasst die in [TextBox1] vorgenommenen Eingaben zusammen |
Die Eigenschaft [EnableViewState] der Seite wird auf [false] gesetzt. Auf der Serverseite verarbeiten wir das Klick-Ereignis auf die Schaltfläche [btnAjouter]:
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' man fügt den Inhalt von [textBox1] zu dem von [TextBox2] hinzu
textbox2.text=textbox2.text + textbox1.text+controlchars.crlf
End Sub
Um diesen Code zu verstehen, muss man sich die Verarbeitung des POST eines Formulars in Erinnerung rufen. Die Prozeduren [Page_Init] und [Page_Load] werden zuerst ausgeführt. Danach folgen alle zwischengespeicherten Ereignisprozeduren. Schließlich wird die Prozedur ausgeführt, die das Ereignis verarbeitet, das den Code [POST] ausgelöst hat – in diesem Fall die Prozedur [btnAjouter_Click]. Wenn die Ereignisbehandler ausgeführt werden, haben alle Komponenten der Seite, die einen Wert im Code POST haben, diesen Wert angenommen. Die übrigen haben ihren vorherigen Wert wieder angenommen, sofern ihre Eigenschaft [EnableViewState] auf [true] gesetzt war, oder ihren Entwurfswert, sofern ihre Eigenschaft [EnableViewState] auf [false] gesetzt war. Hier werden die Werte der Felder [TextBox1] und [TextBox2] Teil des vom Kunden erstellten POST. Ebenso wird im obigen Code [textbox1.text] den vom Kunden gebuchten Wert annehmen, ebenso wie [textbox2.text]. Die Prozedur [btnAjouter_Click] setzt in das Feld [TextBox2] den für [TextBox2] gesendeten Wert, addiert zu dem für [TextBox1] gesendeten Wert, addiert zur Zeilenendemarkierung [ControlChars.CrLf], das im Namensraum [Microsoft.VisualBasic] definiert ist. Dieser Namensraum muss nicht importiert werden, da er vom Webserver standardmäßig importiert wird.
Der endgültige Code von [form5.aspx] lautet wie folgt:
<%@ Page Language="VB" EnableViewState="False" %>
<script runat="server">
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' Der Inhalt von [textBox1] wird zu dem von [TextBox2] hinzugefügt
textbox2.text+=textbox1.text+controlchars.crlf
End Sub
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter" EnableViewState="False"></asp:Button>
<asp:TextBox id="TextBox1" runat="server" TextMode="Password" Width="353px" EnableViewState="False"></asp:TextBox>
</p>
<p>
<asp:TextBox id="TextBox2" runat="server" TextMode="MultiLine" Width="419px" Height="121px" EnableViewState="False"></asp:TextBox>
</p>
</form>
</body>
</html>
Etwas weiter oben wurde ein Screenshot einer Ausführung gezeigt.
7.7. Die Komponente DropDownList
Mit dem Tag <asp:DropDownList> lässt sich eine Dropdown-Liste in den Layout-Code einer Seite einfügen. Wir erstellen eine Seite mit dem Namen [form6.aspx], um das folgende Layout zu erhalten:

Nr. | Name | Typ | Eigenschaften | Rolle |
1 | DropDownList | AutoPostback=true EnableViewState=true | Dropdown-Liste | |
2 | Beschriftung | EnableViewState=false | Informationsmeldung |
Der generierte Darstellungscode lautet wie folgt:
Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
</p>
<p>
<asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
</p>
</form>
</body>
</html>
Derzeit enthält die Dropdown-Liste keine Elemente. Wir werden sie in der Prozedur [Page_Load] füllen. Dazu müssen wir einige der Eigenschaften und Methoden der Klasse [DropDownList] kennen:
Sammlung vom Typ [ListItemCollection] der Elemente der Dropdown-Liste. Die Elemente dieser Sammlung sind vom Typ [ListItem]. | |
Anzahl der Elemente der Sammlung [Items] | |
Element Nr. i der Liste – vom Typ [ListItem] | |
um ein neues Element [ListItem] zur Sammlung [Items] hinzuzufügen | |
um alle Elemente aus der Sammlung [Items] zu löschen | |
um das Element Nr. i aus der Sammlung [Items] zu löschen | |
1. Element [ListItem] der Sammlung [Items], dessen Eigenschaft [Selected] den Wert „wahr“ hat | |
Nummer des Elements [SelectedItem] in der Sammlung [Items] |
Die Elemente der Sammlung [Items] der Klasse [DropDownList] sind vom Typ [ListItem]. Jedes Element [ListItem] erzeugt ein HTML-Tag <option>:
Wir beschreiben einige Eigenschaften und Methoden der Klasse [ListItem]:
Konstruktor – erstellt ein Element [ListItem] mit den Eigenschaften [texte] und [value]. Ein Element ListItem(T,V) erzeugt das Tag HTML <option value="V">T</option>. Die Klasse [ListITem] ermöglicht es somit, die Elemente einer Liste HTML zu beschreiben | |
(Boolesche Werte). Ist der Wert „wahr“, erhält die entsprechende Option in der Liste HTML das Attribut [selected="selected"]. Dieses Attribut weist den Browser an, das entsprechende Element in der Liste HTML | |
der Text T der Option HTML <option value="V" [selected="selected"]>T</option> | |
den Wert „V“ des Attributs [Value] der Option HTML <option value="V" [selected="selected"]>T</option> |
Wir haben genügend Informationen, um in der Prozedur [Page_Load] der Seite den Code zum Befüllen der Dropdown-Liste [DropDownList1] zu schreiben:
Sub Page_Load(sender As Object, e As EventArgs)
' Die Combobox wird ausgefüllt, wenn die Funktion zum ersten Mal aufgerufen wird
if not IsPostBack then
dim valeurs() as String = {"1","2","3","4"}
dim textes() as String = {"un","deux","trois","quatre"}
dim i as integer
for i=0 to valeurs.length-1
DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i)))
next
end if
end sub
Wenn die Komponente [DropDownList1] wie oben initialisiert wird, lautet ihre Übersetzung HTML wie folgt:
<select name="DropDownList1" id="DropDownList1" onchange="__doPostBack('DropDownList1','')" language="javascript">
<option value="1">un</option>
<option value="2">deux</option>
<option value="3">trois</option>
<option value="4">quatre</option>
</select>
Wir wissen, dass die Prozedur [Page_Load] bei jedem Aufruf der Seite [form6.aspx] ausgeführt wird. Diese wird zunächst von einem GET aufgerufen und anschließend jedes Mal von einem POST, wenn der Benutzer ein neues Element aus der Dropdown-Liste auswählt. Muss der Code zum Ausfüllen dieser Liste in [Page_Load] jedes Mal ausgeführt werden? Die Antwort hängt vom Attribut [EnableViewState] der Komponente [DropDownList1] ab. Ist dieses Attribut auf „wahr“ gesetzt, wird der Status der Komponente [DropDownList1] über mehrere Abfragen hinweg im versteckten Feld [__VIEWSTATE] beibehalten. Dieser Status umfasst zwei Elemente:
- die Liste aller Werte der Dropdown-Liste
- den Wert des in dieser Liste ausgewählten Elements
Es erscheint daher verlockend, die Eigenschaft [EnableViewState] der Komponente [DropDownList1] auf [true] zu setzen, um die in die Liste einzufügenden Werte nicht neu berechnen zu müssen. Das Problem dabei ist jedoch, dass die Prozedur [Page_Load] bei jedem Aufruf der Seite [form6.aspx] ausgeführt wird, sodass diese Werte dennoch berechnet werden. Das Objekt [Page], von dem [form6.aspx] eine Instanz ist, verfügt über ein Attribut [IsPostBack] mit einem booleschen Wert. Ist dieses Attribut auf „wahr“ gesetzt, bedeutet dies, dass die Seite von einem POST aufgerufen wurde. Ist der Wert „falsch“, bedeutet dies, dass die Seite von einem GET aufgerufen wurde. In unserem Client-Server-System fordert der Client immer dieselbe Seite [form6.aspx] vom Server an. Beim ersten Mal fordert er sie mit einem GET an, bei den folgenden Malen mit einem POST. Daraus lässt sich schließen, dass die Eigenschaft [IsPostBack] dazu dienen kann, den ersten Aufruf GET des Clients zu erkennen. Die Werte der Dropdown-Liste werden nur bei diesem ersten Aufruf generiert. Bei den folgenden Anfragen werden diese Werte durch den Mechanismus von [VIEWSTATE] generiert. In anderen Situationen kann der Inhalt einer Liste von Anfrage zu Anfrage variieren und muss daher bei jeder Anfrage neu berechnet werden. In diesem Fall wird das Attribut [EnableViewState] der Liste auf [false] gesetzt, um eine unnötige doppelte Berechnung des Listeninhalts zu vermeiden – es sei denn, man muss die zuvor in der Liste ausgewählten Elemente kennen, da diese Information im [VIEWSTATE] gespeichert wird.
Das Attribut [AutoPostBack] der Liste [DropDownList1] wurde auf „true“ gesetzt. Das bedeutet, dass der Browser das Formular absendet, sobald er das Ereignis „Änderung des ausgewählten Elements“ in der Dropdown-Liste erkennt. Der Server erkennt seinerseits anhand von [VIEWSTATE] und der übermittelten Werte, dass sich das in der Komponente [DropDownList1] ausgewählte Element geändert hat. Er löst daraufhin das Ereignis [SelectedIndexChanged] für diese Komponente aus. Wir werden dies mit der folgenden Prozedur verarbeiten:
Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
' Änderung der Auswahl
lblInfo.text="Elément sélectionné : texte="+dropdownlist1.selecteditem.text+ _
" valeur=" + dropdownlist1.selecteditem.value + _
" index="+ dropdownlist1.selectedindex.tostring
End Sub
Bei der Ausführung dieses Vorgangs hat das Objekt [DropDownList1] mithilfe von [VIEWSTATE] seine Elemente vom Typ [ListItem] wiedergefunden. Außerdem ist bei einem dieser Elemente vom Typ [ListItem] das Attribut [Selected] auf „wahr“ gesetzt – nämlich bei dem Element, dessen Wert vom Browser übermittelt wurde. Auf dieses Element kann auf verschiedene Weise zugegriffen werden:
ist das erste Element [ListItem] in der Liste, dessen Attribut [Selected] den Wert „true“ hat | |
entspricht dem Teil [texte] des Tags HTML des Elements <option value="...">text</option>, das vom Benutzer ausgewählt wurde | |
entspricht dem Teil [value] des Tags HTML des Elements <option value="...">Text</option>, der vom Benutzer ausgewählt wurde | |
Nummer in der Sammlung [DropDownList1.Items] des ersten Elements [ListItem], dessen Attribut [Selected] auf „wahr“ gesetzt ist |
Der endgültige Code von [form6.aspx] lautet wie folgt:
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
' Das Kombinationsfeld wird ausgefüllt, wenn der Anruf zum ersten Mal erfolgt
if not IsPostBack then
dim valeurs() as String = {"1","2","3","4"}
dim textes() as String = {"un","deux","trois","quatre"}
dim i as integer
for i=0 to valeurs.length-1
DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i)))
next
end if
end sub
Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs)
' Änderung der Auswahl
lblInfo.text="Elément sélectionné : texte="+dropdownlist1.selecteditem.text+ _
" valeur=" + dropdownlist1.selecteditem.value + _
" index="+ dropdownlist1.selectedindex.tostring
End Sub
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList>
</p>
<p>
<asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label>
</p>
</form>
</body>
</html>
7.8. Die Komponente ListBox
Mit dem Tag <asp:ListBox> lässt sich eine Liste in den Layout-Code einer Seite einfügen. Wir erstellen [form7.aspx], um das folgende Layout zu erhalten:

Nr. | Name | Typ | Eigenschaften | Rolle |
1 | TextBox | EnableViewState=false | Eingabefeld | |
2 | Schaltfläche | Schaltfläche [submit], die den Inhalt von txtSaisie in Liste 1 überträgt, sofern dieser nicht leer ist. | ||
3 | ListBox | EnableViewState=true SelectionMode=Single | Liste mit Einzelauswahl | |
4 | ListBox | EnableViewState=true SelectionMode=Multiple | Liste mit mehreren Auswahlmöglichkeiten | |
5 | Schaltfläche | Schaltfläche [submit], die das ausgewählte Element aus [liste 1] in [liste 2] überträgt | ||
6 | Schaltfläche | Schaltfläche [submit], die die ausgewählten Elemente aus [liste 2] in [liste 1] überträgt |
Der generierte Darstellungscode lautet wie folgt:
<%@ Page Language="VB" %>
<script runat="server">
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Tapez un texte pour l'inclure dans Liste 1 :
<asp:TextBox id="txtSaisie" runat="server" EnableViewState="False"></asp:TextBox>
</p>
<p>
<asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter"></asp:Button>
</p>
<p>
<table>
<tbody>
<tr>
<td>
<p align="center">
Liste 1
</p>
</td>
<td>
</td>
<td>
<p align="center">
Liste 2
</p>
</td>
</tr>
<tr>
<td>
<asp:ListBox id="ListBox1" runat="server"></asp:ListBox>
</td>
<td>
<p>
<asp:Button id="btn1vers2" onclick="btn1vers2_Click" runat="server" Text="-->"></asp:Button>
</p>
<p>
<asp:Button id="btn2vers1" onclick="btn2vers1_Click" runat="server" Text="<--"></asp:Button>
</p>
</td>
<td>
<p>
<asp:ListBox id="ListBox2" runat="server" SelectionMode="Multiple"></asp:ListBox>
</p>
</td>
</tr>
<tr>
<td>
<p align="center">
<asp:Button id="btnRaz1" onclick="btnRaz1_Click" runat="server" Text="Effacer"></asp:Button>
</p>
</td>
<td>
</td>
<td>
<p align="center">
<asp:Button id="btnRaz2" onclick="btnRaz2_Click" runat="server" Text="Effacer"></asp:Button>
</p>
</td>
</tr>
</tbody>
</table>
</p>
</form>
</body>
</html>
Die Klasse [ListBox] ist von derselben Klasse [ListControl] abgeleitet wie die zuvor behandelte Klasse [DropDownList]. Darin finden sich alle Eigenschaften und Methoden wieder, die wir bereits für [DropDownList] betrachtet haben, da sie eigentlich zu [ListControl] gehörten. Eine neue Eigenschaft taucht auf:
legt den Auswahlmodus der Liste HTML <select> fest, die aus der Komponente generiert wird. Wenn SelectionMode=Single ist, kann nur ein Element ausgewählt werden. Wenn SelectionMode=Multiple ist, können mehrere Elemente ausgewählt werden. Zu diesem Zweck wird das Attribut [multiple="multiple"] im Tag <select> der Liste HTML generiert. |
Behandeln wir nun die Ereignisse. Ein Klick auf die Schaltfläche [Ajouter] wird von der folgenden Prozedur [btnAjouter_Click] verarbeitet:
Sub btnAjouter_Click(sender As Object, e As EventArgs)
' Hinzufügen zur Liste 1
dim texte as string=txtSaisie.text.trim
if texte<> "" then ListBox1.Items.Add(New ListItem(texte))
' txtSaisie löschen
txtSaisie.text=""
End Sub
Wenn der in [txtSaisie] eingegebene Text nicht die leere Zeichenfolge oder ein Leerzeichen ist, wird der Liste [ListBox1] ein neues Element hinzugefügt. Wir wissen, dass wir ein Element vom Typ [ListItem] hinzufügen müssen. Zuvor hatten wir den Konstruktor [ListItem(T as String, V as String)] verwendet, um eine ähnliche Aufgabe zu erledigen. Ein solches Element erzeugt das Tag HTML [<option value="V">T</option>]. Hier verwenden wir den Konstruktor [ListItem(T as String)], der die Tags HTML, [<option value="T">T</option>] und c.a.d erzeugt. wobei der Text [T] der Option als Wert der Option übernommen wird. Sobald der Inhalt von [txtSaisie] zur Liste [ListBox1] hinzugefügt wurde, wird das Feld [txTSaisie] geleert.
Klicks auf die Schaltflächen [Effacer] werden durch die folgenden Prozeduren verarbeitet:
Sub btnRaz1_Click(sender As Object, e As EventArgs)
' Liste 1 löschen
ListBox1.Items.Clear
End Sub
Sub btnRaz2_Click(sender As Object, e As EventArgs)
' Liste 2 löschen
ListBox2.Items.Clear
End Sub
Klicks auf die Schaltflächen zum Wechseln zwischen den Listen werden hingegen von den folgenden Prozeduren verarbeitet:
Sub btn1vers2_Click(sender As Object, e As EventArgs)
' Übertragung des ausgewählten Elements aus Liste 1 in Liste 2
transfert(ListBox1,ListBox2)
End Sub
Sub btn2vers1_Click(sender As Object, e As EventArgs)
' Übertragung des ausgewählten Elements aus Liste 2 in Liste 1
transfert(ListBox2,ListBox1)
End Sub
sub transfert(l1 as listbox, l2 as listbox)
' Übertragung der ausgewählten Elemente aus Liste 1 in Liste 2
' Gibt es etwas zu tun?
if l1.selectedindex=-1 then return
dim i as integer
' Wir fangen am Ende an
for i=l1.items.count-1 to 0 step -1
' Ausgewählt?
if l1.items(i).selected then
' nicht mehr ausgewählt
l1.items(i).selected=false
' Übertragung nach l2
l2.items.add(l1.items(i))
' Löschen in l1
l1.items.removeAt(i)
end if
next
end sub
Da beide Schaltflächen dieselbe Funktion erfüllen – nämlich Elemente von einer Liste in eine andere zu übertragen –, lässt sich dies auf einen einzigen Übertragungsvorgang mit zwei Parametern reduzieren:
- l1 vom Typ [ListBox], das ist die Quellliste
- l2 vom Typ [ListBox], die die Zielliste darstellt
Zunächst wird geprüft, ob in der Liste l1 mindestens ein Element ausgewählt ist; ist dies nicht der Fall, gibt es nichts zu tun. Dazu wird die Eigenschaft [l1.selectedindex] untersucht, die die Nummer des ersten ausgewählten Elements in der Liste angibt. Ist kein Element ausgewählt, beträgt ihr Wert -1. Ist mindestens ein Element in l1 ausgewählt, wird die Übertragung nach l2 durchgeführt. Dazu durchläuft man die gesamte Liste der Elemente von l1 und prüft für jedes Element, ob sein Attribut [selected] auf „wahr“ gesetzt ist. Wenn ja, wird sein Attribut [selected] auf [false] gesetzt, anschließend wird es in die Liste l2 kopiert und schließlich aus der Liste l1 gelöscht. Diese Löschung führt zu einer Neunummerierung der Elemente der Liste l1. Deshalb wird die Liste der Elemente von l1 rückwärts durchlaufen. Würde man sie vorwärts durchlaufen und das Element Nr. 10 löschen, würde das Element Nr. 11 zur Nr. 10 und die Nr. 12 zum Element Nr. 11 werden. Nachdem das Element Nr. 10 verarbeitet wurde, wird unsere vorwärts laufende Schleife das Element Nr. 11 verarbeiten, das, wie gerade erläutert, die ehemalige Nr. 12 ist. Das Element, das zuvor die Nr. 11 hatte und nun die Nr. 10 trägt, wird dabei übersehen. Indem wir die Elemente der Liste l1 in umgekehrter Reihenfolge durchlaufen, vermeiden wir dieses Problem.
7.9. Die Komponenten CheckBox, RadioButton
Mit den Tags <asp:RadioButton> und <asp:CheckBox> lassen sich jeweils ein Optionsfeld und ein Kontrollkästchen in den Layout-Code einer Seite einfügen. Wir erstellen eine Seite mit dem Namen [form8.aspx], um das folgende Layout zu erhalten:

Nr. | Name | Typ | Eigenschaften | Rolle |
1 | RadioButton | RadioButton1.Checked=true RadioButton1.Text=1 RadioButton2.Checked=false RadioButton2.Text=2 RadioButton3.Checked=false RadioButton3.Text=3 für die 3 Schaltflächen: GroupName=radio | Optionsfelder | |
2 | CheckBox | Checked=false für alle CheckBoxA.Text=A CheckBoxB.Text=B CheckBoxC.Text=C | Kontrollkästchen | |
3 | Schaltfläche | Schaltfläche [submit] | ||
4 | ListBox | Informationsliste |
Damit der Browser die drei Optionsfelder als gegenseitig ausschließende Schaltflächen behandelt, müssen sie in einer Optionsfeldgruppe zusammengefasst werden. Dies geschieht mithilfe des Attributs [GroupName] der Klasse [RadioButton]. Der Seitenstatus muss in dieser Anwendung nicht beibehalten werden. Daher fügen wir der Seite das Attribut [EnableViewState="false"] hinzu. Der Darstellungscode lautet wie folgt:
<html>
<head>
</head>
<body>
<form id="frmControls" runat="server">
<h3>Cases à cocher
</h3>
<p>
<asp:RadioButton id="RadioButton1" runat="server" Checked="True" EnableViewState="False" GroupName="radio" Text="1"></asp:RadioButton>
<asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" GroupName="radio" Text="2"></asp:RadioButton>
<asp:RadioButton id="RadioButton3" runat="server" EnableViewState="False" GroupName="radio" Text="3"></asp:RadioButton>
</p>
<p>
<asp:CheckBox id="CheckBoxA" runat="server" EnableViewState="False" Text="A"></asp:CheckBox>
<asp:CheckBox id="CheckBoxB" runat="server" EnableViewState="False" Text="B"></asp:CheckBox>
<asp:CheckBox id="CheckBoxC" runat="server" EnableViewState="False" Text="C"></asp:CheckBox>
</p>
<p>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
<asp:Button id="btnTree" onclick="btnTree_Click" runat="server" Text="Contrôles"></asp:Button>
</p>
<p>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6" Height="131px"></asp:ListBox>
</p>
</form>
</body>
</html>
Wir müssen die Prozedur [btnEnvoyer_Click] schreiben, um das Ereignis [Click] für diese Schaltfläche zu verarbeiten. Der Status eines Optionsfelds oder eines Kontrollkästchens wird durch sein Attribut [Checked] angegeben, ein boolescher Wert, der „wahr“ ist, wenn das Kontrollkästchen aktiviert ist, und „falsch“ andernfalls. Es reicht also aus, in die Liste [lstInfos] den Wert des Attributs [Checked] der sechs Optionsfelder und Kontrollkästchen einzutragen. Da dies keine besonderen Schwierigkeiten bereitet, gehen wir einen Schritt weiter:
<script runat="server">
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' Informationen werden in die Listbox eingefügt
for each c as control in FindControl("frmControls").controls
' Ist das Steuerelement von CheckBox abgeleitet?
if TypeOf(c) is CheckBox then
lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString)
end if
next
End Sub
</script>
Die Seite kann als Baumstruktur aus Steuerelementen betrachtet werden. In unserem Beispiel enthält unsere Seite Texte und serverseitige Steuerelemente. Die Texte werden als ein spezielles Steuerelement namens [LiteralControl] angesehen. Jeder noch so kleine Text erzeugt dieses Steuerelement, selbst eine Folge von Leerzeichen zwischen zwei Steuerelementen. Jedes Steuerelement verfügt über ein Attribut ID, das es identifiziert. Es ist das Attribut ID, das in den Tags erscheint:
Wenn wir die Steuerelemente [LiteralControl] außer Acht lassen, weist die untersuchte Seite folgende Steuerelemente auf:
- [HtmlForm], bei dem es sich um das Formular [ID=frmControls] handelt. Dieses ist wiederum ein Container für Steuerelemente. Es enthält die folgenden Steuerelemente:
-- [ID=RadioButton1] vom Typ [RadioButton]
-- [ID=RadioButton2] vom Typ [RadioButton]
-- [ID=RadioButton3] vom Typ [RadioButton]
-- [ID=CheckBoxA] vom Typ [CheckBox]
-- [ID=CheckBoxA] vom Typ [CheckBox]
-- [ID=CheckBoxA] vom Typ [CheckBox]
-- [ID=btnEnvoyer] vom Typ [Button]
Ein Steuerelement verfügt über folgende Eigenschaften:
gibt die Sammlung der untergeordneten Steuerelemente von [Control] zurück, sofern vorhanden | |
gibt das Steuerelement zurück, das durch ID identifiziert wird und sich an der Wurzel des Baums der untergeordneten Steuerelemente von [Control] befindet. Im obigen Beispiel: Page.FindControl("frmControls") bezeichnet den Container [HtmlForm]. Um das Optionsfeld [RadioButton1] zu erreichen, muss Folgendes geschrieben werden Page.FindControl("frmControls").FindControl("RadioButton1") | |
ID von [Control] |
Kehren wir zum Code der Prozedur [btnEnvoyer_Click] zurück:
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' Informationen werden in die Listbox eingefügt
for each c as control in FindControl("frmControls").controls
' Ist das Steuerelement von CheckBox abgeleitet?
if TypeOf(c) is CheckBox then
lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString)
end if
next
End Sub
Wir möchten den Status der Optionsfelder und Kontrollkästchen im Formular anzeigen. Dazu durchlaufen wir alle Steuerelemente des Formulars. Ist das aktuelle Steuerelement ein von [CheckBox] abgeleiteter Typ, zeigen wir dessen Eigenschaft [Checked] an. Da die Klasse [RadioButton] von der Klasse [CheckBox] abgeleitet ist, gilt die Überprüfung für beide Steuerelementtypen. Der oben gezeigte Screenshot zeigt ein Ausführungsbeispiel.
7.10. Die Komponenten CheckBoxList, RadioButtonList
Manchmal möchte man den Benutzer dazu bringen, zwischen Werten zu wählen, die zum Zeitpunkt der Seitenerstellung noch nicht bekannt sind. Diese Auswahlmöglichkeiten stammen aus einer Konfigurationsdatei, einer Datenbank usw. und sind erst zum Zeitpunkt der Ausführung bekannt. Es gibt Lösungen für dieses Problem, und wir sind ihnen begegnet. Die Einfachauswahlliste eignet sich gut, wenn der Benutzer nur eine Auswahl treffen kann, und die Mehrfachauswahlliste, wenn er mehrere auswählen kann. Aus ästhetischen Gründen und wenn die Anzahl der Auswahlmöglichkeiten nicht groß ist, möchte man möglicherweise Radiobuttons anstelle der Einfachauswahlliste oder Kontrollkästchen anstelle der Mehrfachauswahlliste verwenden. Dies ist mit den Komponenten [CheckBoxList] und [RadioButtonList] möglich.
Die Klassen [CheckBoxList] und [RadioButtonList] leiten sich von derselben Klasse [ListControl] ab wie die zuvor behandelten Klassen [DropDownList] und [ListBox]. Daher werden wir einige der Eigenschaften und Methoden wiederfinden, die wir bei diesen Klassen betrachtet haben – nämlich diejenigen, die eigentlich zu [ListControl] gehörten.
Sammlung vom Typ [ListItemCollection] der Elemente der Dropdown-Liste. Die Elemente dieser Sammlung sind vom Typ [ListItem]. | |
Anzahl der Elemente der Sammlung [Items] | |
Element Nr. i der Liste – vom Typ [ListItem] | |
um ein neues Element [ListItem] zur Sammlung [Items] hinzuzufügen | |
um alle Elemente aus der Sammlung [Items] zu löschen | |
um das Element Nr. i aus der Sammlung [Items] zu löschen | |
1. Element [ListItem] der Sammlung [Items], dessen Eigenschaft [Selected] den Wert „wahr“ hat | |
Nummer des vorhergehenden Elements in der Sammlung [Items] |
Bestimmte Eigenschaften sind spezifisch für die Klassen [CheckBoxList] und [RadioButtonList]:
[horizontal] oder [vertical] für horizontale oder vertikale Listen. |
Die Elemente der Sammlung [Items] sind vom Typ [ListItem]. Jedes Element vom Typ [ListItem] erzeugt ein unterschiedliches Tag, je nachdem, ob es sich um ein Objekt vom Typ [CheckBoxList] oder [RadioButtonList] handelt:
Oder
Wir beschreiben einige Eigenschaften und Methoden der Klasse [ListItem]:
Konstruktor – erstellt ein Element [ListItem] mit den Eigenschaften „text“ und „value“. Ein Element ListItem(T,V) erzeugt je nach Fall das Tag HTML <input type="checkbox" value="V">T oder <input type="radio" value="V">T. | |
Boolescher Wert. Ist dieser Wert „wahr“, erhält die entsprechende Option in der Liste HTML das Attribut [selected="selected"]. Dieses Attribut weist den Browser an, dass das entsprechende Element in der Liste HTML als ausgewählt angezeigt werden soll | |
der Text T der Option HTML <input type=".." value="V" [selected="selected"]>T | |
der Wert des Attributs „Value“ der Option HTML <input type=".." value="V" [selected="selected"]>T |
Wir möchten die folgende Seite [form8b.aspx] erstellen:

Nr. | Name | Typ | Eigenschaften | Rolle |
1 | RadioButtonList | EnableViewState=true RepeatDirection=horizontal | Liste mit Optionsfeldern | |
2 | CheckBoxList | EnableViewState=true RepeatDirection=horizontal | Liste mit Kontrollkästchen | |
3 | Schaltfläche | Schaltfläche [submit], die in [4] die Liste der in beiden Listen ausgewählten Elemente anzeigt | ||
4 | ListBox | EnableViewState=false | Werteliste |
Der Code für die Darstellung der Seite lautet wie folgt:
<%@ Page Language="VB" autoeventwireup="false" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
<form id="frmControls" runat="server">
<h3>Listes de cases à cocher
</h3>
<p>
<asp:RadioButtonList id="RadioButtonList1" runat="server" RepeatDirection="Horizontal"></asp:RadioButtonList>
</p>
<p>
<asp:CheckBoxList id="CheckBoxList1" runat="server" RepeatDirection="Horizontal"></asp:CheckBoxList>
</p>
<p>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
</p>
<p>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6"></asp:ListBox>
</p>
</form>
</body>
</html>
Der Prüfcode lautet wie folgt:
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs) handles MyBase.Load
' Die Listen werden gefüllt, wenn das Steuerelement zum ersten Mal aufgerufen wird
if not IsPostBack then
' Texte für die Liste RadioButton
dim textesRadio() as String = {"1","2","3","4"}
' Texte für die Liste RadioButton
dim textesCheckBox() as String = {"un","deux","trois","quatre"}
' Ausfüllen der Radiobutton-Liste
dim i as integer
for i=0 to textesRadio.length-1
RadioButtonList1.Items.Add(new ListItem(textesRadio(i)))
next
' Auswahl von Element Nr. 1
RadioButtonList1.SelectedIndex=1
' Ausfüllen der Checkbox-Liste
for i=0 to textesCheckBox.length-1
CheckBoxList1.Items.Add(new ListItem(textesCheckBox(i)))
next
end if
end sub
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' Informationen werden in die Listbox „lstinfos“ eingefügt
affiche(RadioButtonList1)
affiche(CheckBoxList1)
End Sub
sub affiche(l1 as ListControl)
' Zeigt die Werte der ausgewählten Elemente aus l1 an
' Gibt es hier etwas zu tun?
if l1.selectedindex=-1 then return
dim i as integer
' Wir fangen am Ende an
for i= 0 to l1.items.count-1
' Ausgewählt?
if l1.items(i).selected then
lstInfos.Items.Add("["+TypeName(l1)+"] ["+l1.items(i).text+"] sélectionné")
end if
next
end sub
</script>
Im Prozedur [Page_Load], die bei jedem Aufruf der Seite ausgeführt wird, werden die beiden Listen initialisiert. Um zu vermeiden, dass dies bei jedem Aufruf erneut geschieht, wird die Eigenschaft [IsPostBack] verwendet, um die Initialisierung nur beim ersten Mal durchzuführen. Bei den folgenden Aufrufen werden die Listen automatisch durch den Mechanismus von [VIEWSTATE] neu generiert. Sobald die Seite angezeigt wird, setzt der Benutzer bestimmte Kontrollkästchen und klickt auf die Schaltfläche [Envoyer]. Die Werte des Formulars werden dann an das Formular selbst gesendet. Nach der Ausführung von [Page_Load] wird die Prozedur [btnEnvoyer_Click] ausgeführt. Diese ruft die Prozedur [affiche] auf, um die Liste [lstInfos] zu füllen. Diese erhält als Parameter ein Objekt vom Typ [ListControl], wodurch ihr sowohl ein Objekt vom Typ [RadioButtonList] als auch ein Objekt vom Typ [CheckBoxList] – beides von [ListControl] abgeleitete Klassen – übermittelt werden können. Die Liste [lstInfos] kann ihr Attribut von [EnableViewState] auf [false] setzen, da ihr Status zwischen den verschiedenen Abfragen nicht beibehalten werden muss.
7.11. Die Panel-Komponenten, LinkButton
Mit dem Tag <asp:panel> lässt sich ein Container für Steuerelemente in eine Seite einfügen. Der Vorteil dieses Containers besteht darin, dass bestimmte seiner Eigenschaften für alle darin enthaltenen Steuerelemente gelten. Dies gilt auch für seine Eigenschaft [Visible]. Diese Eigenschaft existiert für jedes Serversteuerelement. Verfügt ein Container über die Eigenschaft [Visible=false], wird jedes seiner Steuerelemente durch seine eigene Eigenschaft [Visible] gesteuert. Verfügt er über die Eigenschaft [Visible=false], werden der Container und sein gesamter Inhalt nicht angezeigt. Dies kann einfacher sein, als die Eigenschaft [Visible] für jedes einzelne Steuerelement des Containers zu verwalten.
Mit dem Tag <asp:LinkButton> lässt sich ein Link in den Layout-Code einer Seite einfügen. Es hat eine ähnliche Funktion wie die Schaltfläche [Button]. Es löst nämlich dank einer damit verknüpften JavaScript-Funktion ein clientseitiges POST aus. Wir erstellen eine Seite [form9.aspx], um folgende Darstellung zu erhalten:

Nr. | Name | Typ | Eigenschaften | Rolle |
1 | Panel | EnableViewState=true | Steuerelement-Container | |
2 | ListBox | EnableViewState=true | eine Liste mit drei Werten | |
3 | LinkButton | EnableViewState=false | Link zum Ausblenden des Containers |
Wenn der Container ausgeblendet ist, erscheint ein neuer Link:

Nr. | Name | Typ | Eigenschaften | Rolle |
4 | LinkButton | EnableViewState=false | Link zum Anzeigen des Containers |
Der Code für die Darstellung der Seite lautet wie folgt:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
<asp:Panel id="Panel1" runat="server" BorderStyle="Ridge" BorderWidth="1px">
<p>
Conteneur
</p>
<p>
<asp:ListBox id="ListBox1" runat="server">
<asp:ListItem Value="1">un</asp:ListItem>
<asp:ListItem Value="2">deux</asp:ListItem>
<asp:ListItem Value="3" Selected="True">trois</asp:ListItem>
</asp:ListBox>
</p>
</asp:Panel>
</p>
<p>
<asp:LinkButton id="lnkVoir" onclick="lnkVoir_Click" runat="server">Voir le conteneur</asp:LinkButton>
</p>
<p>
<asp:LinkButton id="lnkCacher" onclick="lnkCacher_Click" runat="server">Cacher le conteneur</asp:LinkButton>
</p>
</form>
</body>
</html>
Beachten Sie, dass dieser Code die Liste [ListBox1] mit drei Werten initialisiert. Die Ereignisbehandler [Clic] für die beiden Links lauten wie folgt:
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
...
end sub
Sub lnkVoir_Click(sender As Object, e As EventArgs)
' zeigt Container 1 an
panel1.Visible=true
' Ändert die Links
lnkVoir.visible=false
lnkCacher.visible=true
End Sub
Sub lnkCacher_Click(sender As Object, e As EventArgs)
' blendet Container 1 aus
panel1.Visible=false
' Ändert Links
lnkVoir.visible=true
lnkCacher.visible=false
End Sub
</script>
Wir verwenden die Prozedur [Page_Load], um das Formular zu initialisieren. Dies geschieht bei der ersten Abfrage (IsPostBack=false):
<%@ Page Language="VB" %>
<script runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
' beim ersten Mal
if not IsPostBack then
' wird der Container angezeigt
lnkVoir_Click(nothing,nothing)
end if
end sub
.....
</script>
7.12. Weiter geht’s...
In den vorangegangenen Abschnitten wurden eine Reihe von Serverkomponenten vorgestellt. Dabei wurden jeweils nur einige ihrer Eigenschaften behandelt. Um diese Komponenten genauer zu untersuchen, stehen dem Leser verschiedene Möglichkeiten zur Verfügung:
- die Eigenschaften einer Komponente mit einem IDE wie beispielsweise WebMatrix erkunden. Dieser listet nämlich die wichtigsten Eigenschaften der in einem Formular verwendeten Komponenten auf
- die Dokumentation zu .NET einsehen, um alle Klassen zu entdecken, die den einzelnen Serverkomponenten entsprechen. Dies ist die bevorzugte Methode, um die Komponente vollständig zu beherrschen. Dort findet man die Klassenstruktur, die zu den Komponenten führt, sowie die Eigenschaften, Methoden, Konstruktoren und Ereignisse jeder einzelnen Klasse. Darüber hinaus enthält die Dokumentation manchmal Beispiele.
In diesem Kapitel haben wir die „All-in-One“-Technik [WebMatrix] verwendet, bei der der Darstellungs- und der Steuerungscode einer Seite in derselben Datei zusammengefasst wurden. Generell empfehlen wir diese Methode nicht, sondern die zuvor verwendete sogenannte [codebehind]-Methode, bei der diese beiden Codes in zwei getrennten Dateien untergebracht werden. Wir weisen darauf hin, dass der Vorteil dieser Trennung darin besteht, dass der Steuerungscode kompiliert werden kann, ohne die Webanwendung ausführen zu müssen. Außerdem hatten unsere Beispiele – wie wir bereits zu Beginn des Kapitels erläutert haben – ein ganz besonderes Profil: Sie bestanden aus einer einzigen Seite, bei der es sich um ein Formular handelte, das zwischen Client und Server in aufeinanderfolgenden Anfrage-Antwort-Zyklen ausgetauscht wurde, wobei die erste Client-Anfrage ein GET und die folgenden POST waren.
7.13. Serverkomponenten und Anwendungscontroller
In den vorangegangenen Kapiteln haben wir mehrere Webanwendungen erstellt. Sie wurden alle nach der MVC-Architektur (Model-View-Controller) aufgebaut, die die Anwendung in klar voneinander abgegrenzte Blöcke unterteilt und deren Wartung erleichtert. Damals haben wir unsere Benutzeroberflächen mit Standard-HTML-Tags erstellt. Nach dem, was wir gerade gesehen haben, liegt es nahe, nun Serverkomponenten einsetzen zu wollen. Nehmen wir ein bereits ausführlich behandeltes Problem wieder auf, nämlich die Berechnung einer Steuer. Die MVC-Architektur sah wie folgt aus:

Die Anwendung verfügt über zwei Ansichten: [formulaire.aspx] und [erreurs.aspx]. Die Ansicht [formulaire.aspx] wird angezeigt, wenn die URL [main.aspx] zum ersten Mal aufgerufen wird:

Der Benutzer füllt das Formular aus:

und klickt auf die Schaltfläche [Calculer], um die folgende Antwort zu erhalten:

In einer Anwendung MVC muss jede Anfrage über den Controller laufen, in diesem Fall [main.aspx]. Das bedeutet, dass das Formular [formulaire.aspx], sobald es vom Benutzer ausgefüllt wurde, an [main.aspx] und nicht an [formulaire.aspx] gesendet werden muss. Dies ist schlichtweg nicht möglich, wenn wir die Benutzeroberfläche [formulaire.aspx] mit Serverkomponenten ASP erstellen. Um dies zu verdeutlichen, erstellen wir ein Formular [formtest.aspx] mit einer <asp:button>-Komponente:
<%@ Page Language="VB" EnableViewState="false"%>
<html>
<head>
<title>test</title>
</head>
<body>
<form action="main.aspx" runat="server">
<p>
<asp:Button id="btnTest" runat="server" EnableViewState="false" Text="Test"></asp:Button>
</p>
</form>
</body>
</html>
Beachten Sie das Attribut [action="main.aspx"] des Tags <form..>. Führen wir diese Anwendung aus. Die Übersichtsseite enthält nur eine Schaltfläche:

Sehen wir uns den vom Server gesendeten Code HTML an:
<html>
<head>
<title>test</title>
</head>
<body>
<form name="_ctl0" method="post" action="formtest.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwtNTMwNzcxMzI0Ozs+" />
<p>
<input type="submit" name="btnTest" value="Test" id="btnTest" />
</p>
</form>
</body>
</html>
Man sieht, dass der Code „POST“ im Formular auf das Formular selbst „[action="formtest.aspx"]“ verweist, während wir in „[formtest.aspx]“ den Tag „HTML“ für den Server geschrieben hatten:
Das Attribut [runat="server"] des Tags <form> wird uns durch die Verwendung der Serverkomponenten auferlegt. Es tritt ein Kompilierungsfehler auf, wenn wir dieses Attribut nicht angeben. Wenn wir es angeben, wird das Attribut [action] des Tags <form> ignoriert. Der Server generiert immer ein Attribut [action], das auf das Formular selbst verweist. Daraus lässt sich ableiten, dass in einer MVC-Anwendung keine Formulare verwendet werden können, die mit dem Tag <form ... runat="server"> erstellt wurden. Dieses Tag ist jedoch für alle ASP-Serverkomponenten unverzichtbar, die Benutzereingaben erfassen. Das bedeutet im Grunde, dass man in einer MVC-Anwendung keine ASP-Serverformulare verwenden kann. Das ist eine große Entdeckung. Denn eine der marketingtechnischen Stärken von ASP.NET besteht darin, dass man eine Webanwendung wie eine Windows-Anwendung erstellen kann. Das trifft zu, wenn unsere Anwendung die MVC-Architektur nicht befolgt, gilt aber umso mehr, wenn dies der Fall ist. Die MVC-Architektur scheint jedoch ein grundlegendes Konzept der aktuellen Webentwicklung zu sein, das man kaum ignorieren kann.
Es ist möglich, die Architektur MVC in Verbindung mit Formularen mit ASP-Komponenten für Anwendungen mit wenigen unterschiedlichen Ansichten zu verwenden, und zwar dank des folgenden Kniffs:
- Die Anwendung besteht aus einer einzigen Seite, die als Controller fungiert.
- Die Ansichten werden auf dieser Seite durch verschiedene Container dargestellt, wobei jeder Container einer Ansicht entspricht. Um eine Ansicht anzuzeigen, wird der entsprechende Container sichtbar gemacht und die anderen ausgeblendet
Dies ist eine elegante Lösung, die wir nun in einigen Beispielen umsetzen
7.14. Beispiele für MVC-Anwendungen mit ASP-Serverkomponenten
7.14.1. Beispiel 1
In diesem ersten Beispiel setzen wir die vorgestellten Serverkomponenten um. Die Seite [form10.aspx] sieht wie folgt aus:
![]() | ![]() |
Der Screenshot oben links zeigt das Formular, wie es dem Kunden angezeigt wird. Dieser füllt es aus und übermittelt es über [Envoyer]. Der Server sendet ihm eine Ansicht zurück, die eine Liste der eingegebenen Werte anzeigt (rechter Screenshot). Über einen Link kann der Benutzer zum Formular zurückkehren. Er findet dieses in dem Zustand vor, in dem er es bestätigt hat. Der Darstellungscode von [form10.aspx] lautet wie folgt:
<html>
<head>
<title>Exemple</title> <script language="javascript">
function effacer(){
alert("Vous avez cliqué sur [Effacer]")
}
</script>
</head>
<body>
<p>
Gestion d'un formulaire
</p>
<p>
<hr />
</p>
<form runat="server">
<p>
<asp:Panel id="panelinfo" runat="server" EnableViewState="False">
<p>
Liste des valeurs obtenues
</p>
<p>
<asp:ListBox id="lstInfos" runat="server" EnableViewState="False"></asp:ListBox>
</p>
<p>
<asp:LinkButton id="LinkButton1" onclick="LinkButton1_Click" runat="server">Retour au formulaire</asp:LinkButton>
</p>
<p>
<hr />
</p>
</asp:Panel>
</p>
<p>
<asp:Panel id="panelform" runat="server" >
<table>
<tbody>
<tr>
<td>
Etes-vous marié(e)</td>
<td>
<asp:RadioButton id="rdOui" runat="server" GroupName="rdmarie"></asp:RadioButton>
Oui<asp:RadioButton id="rdNon" runat="server" GroupName="rdmarie" Checked="True"></asp:RadioButton>
Non</td>
</tr>
<tr>
<td>
Cases à cocher</td>
<td>
<asp:CheckBox id="chk1" runat="server"></asp:CheckBox>
1<asp:CheckBox id="chk2" runat="server"></asp:CheckBox>
2<asp:CheckBox id="chk3" runat="server"></asp:CheckBox>
3</td>
</tr>
<tr>
<td>
Champ de saisie</td>
<td>
<asp:TextBox id="txtSaisie" runat="server" MaxLength="20" Columns="20"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Mot de passe</td>
<td>
<asp:TextBox id="txtmdp" runat="server" MaxLength="10" Columns="10" TextMode="Password"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Boîte de saisie</td>
<td>
<asp:TextBox id="txtArea" runat="server" Columns="20" TextMode="MultiLine" Rows="3"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Liste déroulante</td>
<td>
<asp:DropDownList id="cmbValeurs" runat="server"></asp:DropDownList>
</td>
</tr>
<tr>
<td>
Liste à choix unique</td>
<td>
<asp:ListBox id="lstSimple" runat="server"></asp:ListBox>
<asp:Button id="btnRazSimple" onclick="btnRazSimple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button>
</td>
</tr>
<tr>
<td>
Liste à choix multiple</td>
<td>
<asp:ListBox id="lstMultiple" runat="server" SelectionMode="Multiple"></asp:ListBox>
<asp:Button id="razMultiple" onclick="razMultiple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button>
</td>
</tr>
<tr>
<td>
Champ caché</td>
<td>
<asp:Label id="lblSecret" runat="server" visible="False"></asp:Label></td>
</tr>
<tr>
<td>
Bouton simple</td>
<td>
<input id="btnEffacer" onclick="effacer()" type="button" value="Effacer" /></td>
</tr>
<tr>
<td>
Bouton [reset]</td>
<td>
<input id="btnReset" type="reset" value="Rétablir" /></td>
</tr>
<tr>
<td>
Bouton [submit]</td>
<td>
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" EnableViewState="False" Text="Envoyer"></asp:Button>
</td>
</tr>
</tbody>
</table>
</asp:Panel>
</p>
</form>
</body>
</html>
Die Seite enthält zwei Container, einen für jede Ansicht: [panelform] für die Formularansicht, [panelinfo] für die Informationsansicht. Die Liste der Komponenten des Containers [panelForm] lautet wie folgt:
Name | Typ | Eigenschaften | Rolle |
Panel | EnableViewState=true | Formularansicht | |
RadioButton | EnableViewState=true GroupName=rdmarie | Optionsfelder | |
CheckBox | EnableViewState=true | Kontrollkästchen | |
TextBox | EnableViewState=true | Eingabefeld | |
TextBox | EnableViewState=true | geschütztes Eingabefeld | |
TextBox | EnableViewState=true | mehrzeiliges Eingabefeld | |
DropDownList | EnableViewState=true | Dropdown-Liste | |
ListBox | EnableViewState=true SelectionMode=Single | Einfachauswahlliste | |
Schaltfläche | EnableViewState=false | Deaktiviert alle Elemente von lstSimple | |
ListBox | EnableViewState=true SelectionMode=Multiple | Liste mit Mehrfachauswahl | |
Schaltfläche | EnableViewState=false | Alle Elemente aus lstMultiple abwählen | |
Beschriftung | EnableViewState=true Visible=false | verdecktes Feld | |
HTML Standard | zeigt eine Warnmeldung an | ||
Schaltfläche | EnableViewState=false | Schaltfläche [submit] im Formular | |
HTML Standard | Schaltfläche [reset] im Formular |
Die Rolle von [VIEWSTATE] für die Komponenten ist hier von Bedeutung. Alle Komponenten außer den Schaltflächen müssen die Eigenschaft [EnableViewState=true] aufweisen. Um den Grund dafür zu verstehen, muss man sich die Funktionsweise der Anwendung vor Augen führen. Nehmen wir an, das Feld [txtSaisie] verfügt über die Eigenschaft [EnableViewState=false]:
- Der Kunde ruft die Seite [form10.aspx] zum ersten Mal auf. Er erhält die Formularansicht
- er füllt es aus und sendet es über die Schaltfläche [Envoyer] ab. Die Eingabefelder werden daraufhin übermittelt, und der Server weist den Serverkomponenten den übermittelten Wert oder ihren Status [VIEWSTATE] zu, sofern sie einen solchen hatten. So wird dem Feld [txtSaisie] der vom Benutzer eingegebene Wert zugewiesen. Auch für diesen Schritt ist ihr Status [VIEWSTATE] irrelevant. Als Ergebnis des Vorgangs wird die Ansicht [informations] gesendet – eigentlich immer noch die Seite [form10.aspx], jedoch mit einem anderen angezeigten Container.
- Der Benutzer ruft diese neue Ansicht auf und nutzt den Link [Retour au formulaire], um zu dieser zurückzukehren. Daraufhin erfolgt ein POST zu [form10.aspx]. Es gibt dann höchstens einen übermittelten Wert: den vom Benutzer in der Informationsliste ausgewählten Wert, der anschließend nicht weiterverarbeitet wird. Jedenfalls wird kein Feld [txtSaisie] übermittelt.
- Der Server empfängt den Wert POST und weist den Serverkomponenten den übermittelten Wert zu oder, falls vorhanden, ihren Status [VIEWSTATE]. In diesem Fall hat [txtNom] keinen übermittelten Wert. Wenn sein Attribut [EnableViewState] den Wert [false] hat, wird ihm die leere Zeichenfolge zugewiesen. Da er den vom Benutzer eingegebenen Wert haben soll, muss er die Eigenschaft [EnableViewState=true] haben.
Der Container [panelinfo] verfügt über folgende Steuerelemente:
Name | Typ | Eigenschaften | Rolle |
Panel | EnableViewState=false | Informationsansicht | |
ListBox | EnableViewState=false | Übersichtsliste mit den vom Benutzer eingegebenen Werten | |
LinkButton | EnableViewState=false | Link zurück zum Formular |
Wenn man sich während der Tests den Code HTML ansieht, der durch den oben stehenden Präsentationscode generiert wurde, ist man möglicherweise überrascht über den Code, der für das versteckte Feld [lblSecret] generiert wurde:
Die Komponente [lblSecret] wird nicht in den Code HTML übersetzt, da sie die Eigenschaft [Visible=false] besitzt. Da sie jedoch die Eigenschaft [EnableViewState=true] besitzt, wird ihr Wert dennoch im versteckten Feld [__VIEWSATE] gespeichert. Daher kann man ihn abrufen, wie die Tests zeigen werden.
Nun müssen wir noch die Ereignisbehandler schreiben. In [Page_Load] initialisieren wir das Formular:
Sub page_Load(sender As Object, e As EventArgs)
' beim ersten Mal werden die Elemente initialisiert
' Bei den folgenden Malen erhalten diese ihre Werte über VIEWSTATE
if IsPostBack then return
' Formular initialisieren
' Panelinfo wird nicht angezeigt
panelinfo.visible=false
' Paneform wird angezeigt
panelform.visible=true
' Optionsfelder
rdNon.Checked=true
' Kontrollkästchen
chk2.Checked=true
' Eingabefeld
txtSaisie.Text="qqs mots"
' Passwortfeld
txtMdp.Text="ceciestsecret"
' Eingabefeld
txtArea.Text="ligne"+ControlChars.CrLf+"ligne2"+ControlChars.CrLf
' Auswahlfeld
dim i as integer
for i=1 to 4
cmbValeurs.Items.Add(new ListItem("choix"+i.ToString,i.ToString))
next
cmbValeurs.SelectedIndex=1
' Einfachauswahlliste
for i=1 to 7
lstSimple.Items.Add(new ListItem("simple"+i.ToString,i.ToString))
next
lstSimple.SelectedIndex=0
' Mehrfachauswahlliste
for i=1 to 10
lstMultiple.Items.Add(new ListItem("multiple"+i.ToString,i.ToString))
next
lstMultiple.Items(0).Selected=true
lstMultiple.Items(2).Selected=true
' verdecktes Feld
lblSecret.Text="secret"
End Sub
Klick auf die Schaltflächen [lstRazSimple] und [lstMultiple]:
Sub btnRazSimple_Click(sender As Object, e As EventArgs)
' Einfache Liste löschen
lstSimple.SelectedIndex=-1
End Sub
Sub razMultiple_Click(sender As Object, e As EventArgs)
' Mehrfachauswahlliste zurücksetzen
lstMultiple.SelectedIndex=-1
End Sub
Klick auf die Schaltfläche [Envoyer]:
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' Das Infofenster wird eingeblendet und das Formularfenster ausgeblendet
panelinfo.Visible=true
panelform.visible=false
' Die übermittelten Werte werden abgerufen und in lstInfos gespeichert
' Optionsfelder
dim info as string="état marital : "+iif(rdoui.checked,"marié"," non marié")
affiche(info)
' Kontrollkästchen
info=" cases cochées : "+iif(chk1.checked,"1 oui","1 non")+","+ _
iif(chk2.checked,"2 oui","2 non")+","+iif(chk3.checked,"3 oui","3 non")
affiche(info)
' Eingabefeld
affiche("champ de saisie : " + txtSaisie.Text.Trim)
' Passwort
affiche("mot de passe : " + txtMdp.Text.Trim)
' Eingabefeld
dim lignes() as String
lignes=new Regex("\r\n").Split(txtArea.Text.Trim)
dim i as integer
for i=0 to lignes.length-1
lignes(i)="["+lignes(i).Trim+"]"
next
affiche("Boîte de saisie : " + String.Join(",",lignes))
' Auswahlfeld
affiche("éléments sélectionnés dans combo : "+selection(cmbValeurs))
' einfache Liste
affiche("éléments sélectionnés dans liste simple : "+selection(lstSimple))
' Mehrfachliste
affiche("éléments sélectionnés dans liste multiple : "+selection(lstMultiple))
' verstecktes Feld
affiche ("Champ caché : " + lblSecret.Text)
End Sub
sub affiche(msg as String)
' zeigt Meldung in lstInfos an
lstInfos.Items.Add(msg)
end sub
function selection(liste as ListControl) as string
' die Listenelemente werden durchlaufen
' um die ausgewählten Elemente zu finden
dim i as integer
dim info as string=""
for i=0 to liste.Items.Count-1
if liste.Items(i).Selected then info+="[" + liste.Items(i).Text + "]"
next
return info
end function
Und schließlich der Klick auf den Link [Retour vers le formulaire ]:
Sub LinkButton1_Click(sender As Object, e As EventArgs)
' Das Formular wird angezeigt und das Infofenster ausgeblendet
panelform.visible=true
panelinfo.visible=false
End Sub
7.14.2. Beispiel 2
Wir greifen hier eine bereits behandelte Anwendung mit Standardformularen HTML wieder auf. Die Anwendung ermöglicht die Simulation von Steuerberechnungen. Sie basiert auf einer Klasse [impot], auf die wir hier nicht näher eingehen werden. Diese Klasse benötigt Daten, die sie in einer Datenquelle OLEDB findet. Für dieses Beispiel wird es sich um eine Quelle ACCESS handeln.
7.14.2.1. Die Struktur MVC der Anwendung
Die Struktur MVC der Anwendung sieht wie folgt aus:

Die drei Ansichten werden in Form von Containern in den Präsentationscode des Controllers [main.aspx] eingebunden. Daher verfügt diese Anwendung über eine einzige Seite: [main.aspx].
7.14.2.2. Die Ansichten der Webanwendung
Die Ansicht [formulaire] ist das Formular zur Eingabe der Informationen, anhand derer die Steuer eines Benutzers berechnet wird:

Der Benutzer füllt das Formular aus:

Er nutzt die Schaltfläche [Envoyer], um die Berechnung seiner Steuer anzufordern. Daraufhin wird die folgende Ansicht [simulations] angezeigt:

Über den obigen Link kehrt er zum Formular zurück. Er findet es in dem Zustand vor, in dem er es eingegeben hat. Dabei können ihm Eingabefehler unterlaufen:

Diese werden ihm in der Ansicht [erreurs] angezeigt:

Über den obigen Link kehrt er zum Formular zurück. Er findet es in dem Zustand vor, in dem er es eingegeben hat. Er kann neue Simulationen durchführen:

Er erhält dann die Ansicht [simulations] mit einer weiteren Simulation:

Ist die Datenquelle schließlich nicht verfügbar, wird dies dem Benutzer in der Ansicht [erreurs] angezeigt:

7.14.2.3. Der Darstellungscode der Anwendung
Zur Erinnerung: Die Seite [main.aspx] fasst alle Ansichten zusammen. Es handelt sich um ein einziges Formular mit drei Containern:
- [panelform] für die Ansicht [formulaire]
- [panelerreurs] für die Ansicht [erreurs]
- [panelsimulations] für die Ansicht [simulations]
Wir kehren zur Trennung von Darstellungs- und Steuerungscode in zwei separaten Dateien zurück. Die erste befindet sich in [main.aspx] und die zweite in [main.aspx.vb]. Der Code von [main.aspx] lautet wie folgt:
<%@ page codebehind="main.aspx.vb" inherits="vs.main" AutoEventWireUp="false" %>
<HTML>
<HEAD>
<title>Calcul d'impôt </title>
</HEAD>
<body>
<P>Calcul de votre impôt</P>
<HR width="100%" SIZE="1">
<FORM id="Form1" runat="server">
<asp:panel id="panelform" Runat="server">
<TABLE id="Table1" cellSpacing="1" cellPadding="1" border="0">
<TR>
<TD height="19">Etes-vous marié(e)</TD>
<TD height="19">
<asp:RadioButton id="rdOui" runat="server" GroupName="rdMarie"></asp:RadioButton>Oui
<asp:RadioButton id="rdNon" runat="server" GroupName="rdMarie" Checked="True"></asp:RadioButton>Non</TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD>
<asp:TextBox id="txtEnfants" runat="server" MaxLength="3" Columns="3"></asp:TextBox></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD>
<asp:TextBox id="txtSalaire" runat="server" MaxLength="10" Columns="10"></asp:TextBox></TD>
</TR>
</TABLE>
<P>
<asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button>
<asp:Button id="btnEffacer" runat="server" Text="Effacer"></asp:Button></P>
</asp:panel>
<asp:panel id="panelerreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<P>
<asp:Literal id="erreursHTML" runat="server"></asp:Literal></P>
<P></P>
<asp:LinkButton id="lnkForm1" runat="server">Retour au formulaire</asp:LinkButton>
</asp:panel>
<asp:panel id="panelsimulations" runat="server">
<P>
<TABLE>
<TR>
<TH>
Marié</TH>
<TH>
Enfants</TH>
<TH>
Salaire annuel</TH>
<TH>
Impôt à payer (euro)</TH></TR>
<asp:Literal id="simulationsHTML" runat="server"></asp:Literal></TABLE>
<asp:LinkButton id="lnkForm2" runat="server">Retour au formulaire</asp:LinkButton></P>
</asp:panel>
</FORM>
</body>
</HTML>
Wir haben die drei Container abgegrenzt. Es ist zu beachten, dass sie sich alle innerhalb des Tags <form runat="server"> befinden. Dies ist zwingend erforderlich, denn um die Vorteile der Serverkomponenten nutzen zu können, müssen diese in einem solchen Tag platziert werden. Wichtig ist, dass es sich hier um ein einziges Formular handelt, das zwischen dem Client und dem Webserver ausgetauscht wird. Wir befinden uns also genau in der Konfiguration, die in diesem gesamten Kapitel über Serverkomponenten verwendet wird. Sehen wir uns die Komponenten der einzelnen Container im Detail an:
Container [panelform]:
Name | Typ | Eigenschaften | Rolle |
Panel | EnableViewState=true | Formularansicht | |
RadioButton | EnableViewState=true GroupName=rdmarie | Optionsfelder | |
TextBox | EnableViewState=true | Anzahl der Kinder | |
TextBox | EnableViewState=true | Jahresgehalt | |
Schaltfläche | Schaltfläche [submit] im Formular – startet die Steuerberechnung | ||
Schaltfläche | Schaltfläche [submit] im Formular – löscht das Formular |
Container [panelerreurs]:
Name | Typ | Eigenschaften | Rolle |
Panel | EnableViewState=true | Fehleransicht | |
LinkButton | EnableViewState=true | Link zum Formular | |
Literal | Code HTML aus der Fehlerliste |
Container [panelsimulations]:
Name | Typ | Eigenschaften | Rolle |
Panel | EnableViewState=true | Ansicht „Simulationen“ | |
LinkButton | EnableViewState=true | Link zum Formular | |
Literal | Code HTML aus der Liste der Simulationen in einer Tabelle HTML |
7.14.2.4. Der Steuerungscode der Anwendung
Der Anwendungsprüfcode ist auf die Dateien [global.asax.vb] und [main.aspx.vb] verteilt. Die Datei [global.asax] ist wie folgt definiert:
Die Datei [global.asax.vb] lautet wie folgt:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Collections
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' Es wird ein Import-Objekt erstellt
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' Das Objekt wird in die Anwendung eingefügt
Application("objImpot") = objImpot
' kein Fehler
Application("erreur") = False
Catch ex As Exception
'Es ist ein Fehler aufgetreten, der in der Anwendung vermerkt wird
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' Sitzungsbeginn – es wird eine leere Simulationsliste erstellt
Session.Item("simulations") = New ArrayList
End Sub
End Class
Beim Start der Anwendung (erste Anfrage an die Anwendung) wird die Prozedur [Application_Start] ausgeführt. Sie versucht, ein Objekt vom Typ [impot] anzulegen, dessen Daten aus einer Quelle vom Typ OLEDB stammen. Der Leser wird gebeten, Kapitel 5 zu lesen, in dem diese Klasse definiert wurde, falls er dies vergessen hat. Die Erstellung des Objekts [impot] kann fehlschlagen, wenn die Datenquelle nicht verfügbar ist. In diesem Fall wird der Fehler in der Anwendung gespeichert, damit alle nachfolgenden Abfragen wissen, dass diese nicht korrekt initialisiert werden konnte. Wenn die Erstellung erfolgreich verläuft, wird das erstellte Objekt [impot] ebenfalls in der Anwendung gespeichert. Es wird von allen Abfragen zur Steuerberechnung verwendet. Wenn ein Kunde seine erste Abfrage stellt, wird für ihn durch die Prozedur [Application_Start] eine Sitzung angelegt. Diese Sitzung dient dazu, die verschiedenen Steuerberechnungssimulationen zu speichern, die er durchführen wird. Diese werden in einem Objekt [ArrayList] gespeichert, das dem Sitzungsschlüssel „simulations“ zugeordnet ist. Zu Beginn der Sitzung wird dieser Schlüssel einem leeren Objekt [ArrayList] zugeordnet. Die für die Anwendung erforderlichen Informationen werden in dessen Konfigurationsdatei [wenConfig] abgelegt:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="chaineConnexion" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\webforms\vs\impots5\impots.mdb" />
</appSettings>
</configuration>
Der Schlüssel [chaineConnexion] bezeichnet die Verbindungszeichenfolge zur Quelle OLEDB. Der andere Teil des Kontrollcodes befindet sich in [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents rdOui As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdNon As System.Web.UI.WebControls.RadioButton
Protected WithEvents txtEnfants As System.Web.UI.WebControls.TextBox
Protected WithEvents txtSalaire As System.Web.UI.WebControls.TextBox
Protected WithEvents btnCalculer As System.Web.UI.WebControls.Button
Protected WithEvents btnEffacer As System.Web.UI.WebControls.Button
Protected WithEvents panelform As System.Web.UI.WebControls.Panel
Protected WithEvents lnkForm1 As System.Web.UI.WebControls.LinkButton
Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
Protected WithEvents panelerreurs As System.Web.UI.WebControls.Panel
Protected WithEvents panelsimulations As System.Web.UI.WebControls.Panel
Protected WithEvents simulationsHTML As System.Web.UI.WebControls.Literal
Protected WithEvents erreursHTML As System.Web.UI.WebControls.Literal
' Lokale Variablen
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
End Sub
Private Sub afficheFormulaire()
...
End Sub
Private Sub afficheSimulations(ByRef simulations As ArrayList, ByRef lien As String)
...
End Sub
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
...
End Sub
Private Function checkData() As ArrayList
...
End Function
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
....
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
...
End Sub
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
...
End Sub
Private Sub razForm()
...
End Sub
End Class
Le premier événement traité par le code est [Page_Load] :
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Zunächst wird geprüft, in welchem Zustand sich die Anwendung befindet
If CType(Application("erreur"), Boolean) Then
' Die Anwendung konnte nicht initialisiert werden
' Die Fehleransicht wird angezeigt
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, "")
Exit Sub
End If
' Keine Fehler – bei der ersten Anfrage wird das Formular angezeigt
If Not IsPostBack Then afficheFormulaire()
End Sub
Zur Erinnerung: Wenn die Prozedur [Page_Load] auf einem POST-Client ausgeführt wird, haben alle Formularkomponenten einen Wert: entweder den vom Client übermittelten Wert, falls vorhanden, oder den vorherigen Wert der Komponente, der über [VIEWSTATE] bereitgestellt wird. In diesem Formular verfügen alle Komponenten über die Eigenschaft [EnableViewState=true]. Bevor wir mit der Bearbeitung der Anfrage beginnen, stellen wir sicher, dass die Anwendung korrekt initialisiert werden konnte. Ist dies nicht der Fall, zeigen wir die Ansicht [erreurs] mit der Prozedur [afficheErreurs] an. Handelt es sich um die erste Anfrage (IsPostBack=false), lassen wir die Ansicht [formulaire] mit [afficheFormulaire] anzeigen.
Die Prozedur zur Anzeige der Ansicht [erreurs] lautet wie folgt:
Private Sub afficheErreurs(ByRef erreurs As ArrayList, ByRef lien As String)
' Der Fehlercontainer wird angezeigt
panelerreurs.Visible = True
Dim i As Integer
erreursHTML.Text = ""
For i = 0 To erreurs.Count - 1
erreursHTML.Text += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
lnkForm1.Text = lien
' Die anderen Container sind ausgeblendet
panelform.Visible = False
panelsimulations.Visible = False
End Sub
Die Prozedur hat zwei Parameter:
- eine Liste von Fehlermeldungen in [erreurs]
- einen Linktext in [lien]
Der für die Fehlerliste zu generierende Code HTML wird in das Literal [erreursHTML] eingefügt. Der Linktext wird hingegen in der Eigenschaft [Text] des Objekts [LinkButton] der Ansicht abgelegt.
Die Prozedur, die die Ansicht [formulaire] anzeigt, lautet wie folgt:
Private Sub afficheFormulaire()
' zeigt das Formular an
panelform.Visible = True
' Die anderen Container sind ausgeblendet
panelerreurs.Visible = False
panelsimulations.Visible = False
End Sub
Dieses Verfahren macht lediglich den Container [panelform] sichtbar. Die Komponenten werden mit ihrem gebuchten oder vorherigen Wert (VIEWSTATE) angezeigt.
Wenn der Benutzer in der Ansicht [formulaire] auf die Schaltfläche [Calculer] klickt, wird ein Übergang von POST zu [main.aspx] durchgeführt. Die Prozedur [Page_Load] wird ausgeführt, anschließend die Prozedur [btnCalculer_Click]:
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
' Die Gültigkeit der eingegebenen Daten wird überprüft
Dim erreurs As ArrayList = checkData()
' Falls Fehler auftreten, werden diese gemeldet
If erreurs.Count <> 0 Then
' die Fehlerseite wird angezeigt
afficheErreurs(erreurs, "Retour au formulaire")
Exit Sub
End If
' keine Fehler – die Steuer wird berechnet
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
' Das Ergebnis wird zu den bestehenden Simulationen hinzugefügt
Dim simulation() As String = New String() {CType(IIf(rdOui.Checked, "oui", "non"), String), _
txtEnfants.Text.Trim, txtSalaire.Text.Trim, impot.ToString}
' Das Ergebnis wird zu den bestehenden Simulationen hinzugefügt
Dim simulations As ArrayList = CType(Session.Item("simulations"), ArrayList)
simulations.Add(simulation)
' Die Simulationen werden in die Sitzung und den Kontext übernommen
Session.Item("simulations") = simulations
' die Ergebnisseite wird angezeigt
afficheSimulations(simulations, "Retour au formulaire")
End Sub
Der Vorgang beginnt mit der Überprüfung der Gültigkeit der Formularfelder mithilfe der Prozedur [checkData], die eine Liste [ArrayList] mit Fehlermeldungen zurückgibt. Ist die Liste nicht leer, wird die Ansicht [erreurs] angezeigt und der Vorgang beendet. Sind die eingegebenen Daten gültig, wird der Steuerbetrag mithilfe des Objekts vom Typ [impot] berechnet, das beim Start der Anwendung gespeichert wurde. Diese neue Simulation wird der Liste der bereits durchgeführten Simulationen hinzugefügt und in der Sitzung gespeichert.
Die Funktion [CheckData] überprüft die Gültigkeit der Daten. Sie gibt eine Liste [ArrayList] mit Fehlermeldungen zurück, die leer ist, wenn die Daten gültig sind:
Private Function checkData() As ArrayList
' Zunächst keine Fehler
Dim erreurs As New ArrayList
' Anzahl der Kinder
Try
Dim nbEnfants As Integer = CType(txtEnfants.Text, Integer)
If nbEnfants < 0 Then Throw New Exception
Catch
erreurs.Add("Le nombre d'enfants est incorrect")
End Try
' Gehalt
Try
Dim salaire As Long = CType(txtSalaire.Text, Long)
If salaire < 0 Then Throw New Exception
Catch
erreurs.Add("Le salaire annuel est incorrect")
End Try
' Die Fehlerliste wird angezeigt
Return erreurs
End Function
Schließlich wird die Ansicht [simulations] durch die folgende Prozedur [afficheSimulations] angezeigt:
Private Sub afficheSimulations(ByRef simulations As ArrayList, ByRef lien As String)
' Zeigt die Ansicht „Simulationen“ an
panelsimulations.Visible = True
' Die anderen Container sind ausgeblendet
panelerreurs.Visible = False
panelform.Visible = False
' Inhalt der Ansicht „Simulationen“
' Jede Simulation ist ein Array mit 4 String-Elementen
Dim simulation() As String
Dim i, j As Integer
simulationsHTML.Text = ""
For i = 0 To simulations.Count - 1
simulation = CType(simulations(i), String())
simulationsHTML.Text += "<tr>"
For j = 0 To simulation.Length - 1
simulationsHTML.Text += "<td>" + simulation(j) + "</td>"
Next
simulationsHTML.Text += "</tr>" + ControlChars.CrLf
Next
' Link
lnkForm2.Text = lien
End Sub
Die Prozedur hat zwei Parameter:
- eine Liste von Simulationen in [simulations]
- einen Linktext in [lien]
Der für die Simulationsliste zu generierende Code HTML wird in das Literal [simulationsHTML] eingefügt. Der Linktext wird hingegen in die Eigenschaft [Text] des Objekts [LinkButton] der Ansicht eingefügt.
Wenn der Benutzer auf die Schaltfläche [Effacer] in der Ansicht [formulaire] klickt, wird die Prozedur [btnEffacer_click] ausgeführt (immer nach [Page_Load]):
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
' zeigt das leere Formular an
razForm()
afficheFormulaire()
End Sub
Private Sub razForm()
' leert das Formular
rdOui.Checked = False
rdNon.Checked = True
txtEnfants.Text = ""
txtSalaire.Text = ""
End Sub
Der obige Code ist so einfach, dass er keiner Erläuterung bedarf. Nun müssen wir noch den Klick auf die Links in den Ansichten [erreurs] und [simulations] behandeln:
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
' Zeigt das Formular an
afficheFormulaire()
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
' zeigt das Formular an
afficheFormulaire()
End Sub
Beide Prozeduren sorgen lediglich dafür, dass die Ansicht [formulaire] angezeigt wird. Wir wissen, dass die Felder dieser Ansicht einen Wert erhalten, der entweder dem für sie übermittelten Wert oder ihrem vorherigen Wert entspricht. Da in diesem Fall das POST des Kunden keine Werte für die Formularfelder übermittelt, erhalten diese ihren vorherigen Wert zurück. Das Formular wird also mit den vom Benutzer eingegebenen Werten angezeigt. Wir erinnern uns, dass wir in der Version ohne Serverkomponenten diese Wiederherstellung selbst vorgenommen hatten.
7.14.2.5. Tests
Alle für die Anwendung erforderlichen Dateien befinden sich in einem Ordner <application-path>: ![]() | Der Ordner „[bin]“ enthält die DLL mit den Klassen „[impot]“, „[impotsData]“ und „[impotsOLEDB]“, die für die Anwendung erforderlich sind: |
Der Leser kann, falls gewünscht, noch einmal Kapitel 5 nachlesen, in dem erklärt wird, wie die oben genannte Datei [impot.dll] erstellt wird. Anschließend wird der Cassini-Server mit den Parametern (<application-path>,/impots5) gestartet. Die URL [http://impots5/main.aspx] wird mit einem Browser aufgerufen:

Wenn man die Datei ACCESS [impots.mdb] in [impots1.mdb] umbenennt, erhält man die folgende Seite:

7.14.3. Beispiel 3
Anhand dieser beiden Beispiele haben wir gezeigt, dass es möglich ist, Webanwendungen zu erstellen, die der Architektur MVC entsprechen und Serverkomponenten verwenden. Das letzte Beispiel zeigt, dass die Lösung mit Serverkomponenten einfacher ist als die Lösung mit Standard-Tags HTML. Unsere beiden Beispiele bestanden nur aus einer Seite mit mehreren Ansichten innerhalb derselben Seite. Eine MVC-Architektur mit mehreren Serverformularen ASP ist möglich, solange es kein Problem darstellt, dass diese die Formularwerte an sich selbst übermitteln. Dies ist sehr häufig bei Anwendungen mit Menüs der Fall. Nehmen wir das folgende Beispiel:

Wir haben auf einer einzigen Seite Links zu den Anwendungen zusammengestellt, die wir bisher geschrieben haben. Diese Art von Anwendung eignet sich gut für eine MVC-Architektur. Es gibt lediglich nicht mehr nur einen, sondern mehrere Controller.

Der Controller [main.aspx] fungiert als Hauptcontroller. Er wird über die Links auf der Startseite der Anwendung aufgerufen. Er führt zunächst die für alle möglichen Aktionen gemeinsamen Vorgänge durch und lässt anschließend die spezifische Aktion ausführen, die dem jeweiligen Link zugeordnet ist. Danach übergibt er die Kontrolle an einen der sekundären Controller, der für die Ausführung der Aktion zuständig ist. Ab diesem Zeitpunkt erfolgt der Datenaustausch zwischen dem Client und diesem bestimmten Controller. Der Haupt-Controller [main.aspx] wird nicht mehr durchlaufen. Wir befinden uns also nicht mehr im Rahmen von MVC mit einem einzigen Controller, der alle Anfragen filtert. Jeder der oben genannten Controller kann mithilfe des von uns vorgestellten Container-Mechanismus mehrere Ansichten innerhalb einer einzigen Seite präsentieren.
Die Tatsache, dass es keinen einzigen Controller mehr gibt, der die an den Client zu sendenden Ansichten auswählt, bringt Nachteile mit sich. Nehmen wir das Beispiel der Fehlerbehandlung. Jede der von der Anwendung bereitgestellten Aktionen kann dazu führen, dass eine Fehleransicht angezeigt wird. Jeder Controller „[applix.aspx]“ wird seine eigene Ansicht „[erreurs]“ haben, da diese lediglich ein spezieller Container der Controller-Seite ist. Es gibt keine Möglichkeit, eine einzige Ansicht „[erreurs]“ zu haben, die von allen einzelnen Anwendungen verwendet würde. Tatsächlich enthält eine solche Ansicht in der Regel einen Link zurück zum fehlerhaften Formular, und dieses muss in den Zustand zurückversetzt werden, in dem es validiert wurde, damit der Benutzer seine Fehler korrigieren kann. Diese Wiederherstellung erfolgt über den Mechanismus von [VIEWSTATE], der jedoch nicht zwischen verschiedenen Controllern funktioniert. Werden die Anwendungen von verschiedenen Personen entwickelt, besteht die Gefahr, dass die Fehlerseiten je nach der vom Benutzer gewählten Aktion unterschiedlich aussehen, was die Einheitlichkeit der gesamten Anwendung beeinträchtigt. Wir werden etwas später sehen, dass ASP.NET eine Lösung für dieses spezielle Problem der gemeinsamen Ansicht bietet. Diese kann Gegenstand einer neuen Serverkomponente sein, die wir selbst erstellen. Es reicht aus, diese Komponente in den verschiedenen Anwendungen zu verwenden, um die Einheitlichkeit der gesamten Anwendung zu gewährleisten. Schwieriger zu handhaben ist das Problem der Reihenfolge der Aktionen. Wenn alle Anfragen über einen einzigen Controller laufen, kann dieser überprüfen, ob die angeforderte Aktion mit der vorherigen kompatibel ist. Dieser Prüfcode befindet sich an einer einzigen Stelle. Hier muss er auf die verschiedenen Controller verteilt werden, was die Wartung der Gesamtanwendung erschwert.
Kehren wir zu unserer oben genannten Anwendung zurück. Die Startseite dieser Anwendung ist eine klassische HTML-Seite:
<html>
<head>
<TITLE>Composants ASP Serveur</TITLE>
<meta name="pragma" content="no-cache">
</head>
<frameset rows="130,*" frameborder="0">
<frame name="banner" src="bandeau.htm" scrolling="no">
<frameset cols="200,*">
<frame name="contents" src="options.htm">
<frame name="main" src="main.htm">
</frameset>
<noframes>
<p id="p1">
Ce jeu de frames HTML affiche plusieurs pages Web. Pour afficher ce jeu de
frames, utilisez un navigateur Web qui prend en charge HTML 4.0 et version
ultérieure.
</p>
</noframes>
</frameset>
</html>
Diese Startseite besteht aus drei Frames mit den Namen „banner“, „contents“ und „main“:
![]() |
Die Seite „[bandeau.htm]“, die im Rahmen „[banner]“ platziert ist, sieht wie folgt aus:

Ihr Code HTML lautet wie folgt:
<html>
<head>
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE" />
<title>bandeau</title>
</head>
<body>
<P>
<TABLE>
<TR>
<TD><IMG alt="logo université d'angers" src="univ01.gif"></TD>
<TD>Composants serveurs ASP</TD>
</TR>
</TABLE>
</P>
<HR>
</body>
</html>
Die Seite [options.htm] befindet sich im Banner [contents]. Es handelt sich um eine Reihe von Links:
![]() | |
Die verschiedenen Links verweisen alle auf den Haupt-Controller [main.aspx], wobei der Parameter [action] die auszuführende Aktion angibt. Es wird festgelegt, dass das Ziel der Links im Frame [main] angezeigt werden soll (target="main").
Die erste Seite, die im Rahmen [main] angezeigt wird, ist [main.htm]:
|
Der Hauptcontroller [main.aspx, main.aspx.vb] lautet wie folgt:
[main.aspx]
[main.aspx.vb]
Public Class main
Inherits System.Web.UI.Page
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' die auszuführende Aktion wird abgerufen
Dim action As String
If Request.QueryString("action") Is Nothing Then
action = "label"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' die Aktion wird ausgeführt
Select Case action
Case "label"
Server.Transfer("form2.aspx")
Case „button“
Server.Transfer("form3.aspx")
Case „textbox1“
Server.Transfer("form4.aspx")
Fall „textbox2“
Server.Transfer("form5.aspx")
Fall „dropdownlist“
Server.Transfer("form6.aspx")
Feld „Listbox“
Server.Transfer("form7.aspx")
Feld „Checkbox“
Server.Transfer("form8.aspx")
Fall „Checkbox-Liste“
Server.Transfer("form8b.aspx")
Feld „Panel“
Server.Transfer("form9.aspx")
Fall „Else“
Server.Transfer("form2.aspx")
End Select
End Sub
End Class
Unser Controller ist einfach aufgebaut. Je nach Wert des Parameters [action] leitet er die Bearbeitung der Anfrage an die entsprechende Seite weiter. Er bietet keinen Mehrwert gegenüber einer Seite HTML mit Links. Dennoch würde bereits das Hinzufügen einer Authentifizierungsseite ausreichen, um den Nutzen zu erkennen. Wenn sich der Benutzer authentifizieren müsste (Benutzername, Passwort), um Zugriff auf die Anwendungen zu erhalten, wäre der Controller [main.aspx] ein geeigneter Ort, um zu überprüfen, ob diese Authentifizierung erfolgt ist.




