Skip to content

11. Die Anwendung [SimuPaie] – Version 7 – ASP.NET / Multi-View / Multi-Page


Empfohlene Lektüre: Referenz [1], Webentwicklung mit ASP.NET 1.1, Abschnitt: Beispiele


Wir betrachten nun eine Version, die funktional identisch ist mit der zuvor besprochenen dreischichtigen ASP.NET-Anwendung [pam-v4-3tier-nhibernate-multivues-monopage], ändern jedoch ihre Architektur wie folgt: Während in der vorherigen Version die Ansichten durch eine einzige ASPX-Seite implementiert wurden, werden sie hier durch drei ASPX-Seiten implementiert.

Die Architektur der vorherigen Anwendung sah wie folgt aus:

Hier haben wir eine MVC-Architektur (Model–View–Controller):

  • [Default.aspx.cs] enthält den Controller-Code. Die Seite [Default.aspx] ist der einzige Kontaktpunkt für den Client. Sie verarbeitet alle Anfragen vom Client.
  • [Saisies, Simulation, Simulations, ...] sind die Views. Diese Views werden hier mithilfe von [View]-Komponenten auf der Seite [Default.aspx] implementiert.

Die Architektur der neuen Version wird wie folgt aussehen:

  • Nur die [Web]-Ebene ändert sich
  • Die Ansichten (das, was dem Benutzer angezeigt wird) bleiben unverändert.
  • Der Controller-Code, der in der vorherigen Version vollständig in [Default.aspx.cs] enthalten war, ist nun auf mehrere Seiten verteilt:
    • [MasterPage.master]: eine Seite, die Elemente kapselt, die den verschiedenen Ansichten gemeinsam sind: das obere Banner mit seinen Menüoptionen
    • [Formulaire.aspx]: die Seite, die das Simulationsformular anzeigt und die Aktionen verwaltet, die auf diesem Formular stattfinden
    • [Simulations.aspx]: die Seite, die die Liste der Simulationen anzeigt und die auf dieser Seite stattfindenden Aktionen verarbeitet
    • [Errors.aspx]: die Seite, die angezeigt wird, wenn ein Fehler bei der Initialisierung der Anwendung auftritt. Auf dieser Seite sind keine Aktionen möglich.

Dies kann als MVC-Architektur mit mehreren Controllern betrachtet werden, während die Architektur der vorherigen Version eine MVC-Architektur mit einem einzigen Controller war.

Die Verarbeitung einer Client-Anfrage erfolgt in folgenden Schritten:

  1. Der Client sendet eine Anfrage an die Anwendung. Diese erfolgt an eine der beiden Seiten [Formulaire.aspx, Simulations.aspx].
  2. Die angeforderte Seite verarbeitet diese Anfrage. Dazu benötigt sie möglicherweise Unterstützung durch die [Business]-Schicht, die ihrerseits die [DAO]-Schicht benötigt, falls Daten mit der Datenbank ausgetauscht werden müssen. Die Anwendung erhält eine Antwort von der [Business]-Schicht.
  3. Auf der Grundlage dieser Antwort wählt sie (3) die Ansicht (= die Antwort) aus, die an den Client gesendet werden soll, und stellt ihm (4) die benötigten Informationen (das Modell) zur Verfügung.
  4. Die Antwort wird an den Client gesendet (5)

11.1. Die Ansichten der Anwendung

Dem Benutzer werden folgende Ansichten angezeigt:

  • - die Ansicht [VueSaisies], die das Simulationsformular anzeigt

Image

  • - die Ansicht [VueSimulation], die zur Anzeige der detaillierten Simulationsergebnisse dient:

Image

  • - die Ansicht [SimulationView], die die vom Client durchgeführten Simulationen auflistet

Image

  • - die Ansicht [EmptySimulationsView], die anzeigt, dass der Client keine oder keine weiteren Simulationen mehr hat:

Image

  • die Ansicht [ErrorView], die einen Fehler bei der Initialisierung der Anwendung anzeigt:

Image

11.2. Erstellen von Ansichten in einem Multi-Controller-Kontext

In der vorherigen Version wurden alle Ansichten von der einzelnen Seite [Default.aspx] aus generiert. Diese Seite enthielt zwei [MultiView]-Komponenten, und die Ansichten bestanden aus einer Kombination von einer oder zwei [View]-Komponenten, die zu diesen beiden [MultiView]-Komponenten gehörten.

Diese Architektur ist zwar bei wenigen Ansichten effektiv, stößt jedoch an ihre Grenzen, sobald die Anzahl der Komponenten, aus denen die verschiedenen Ansichten bestehen, groß wird: Tatsächlich werden bei jeder Anfrage an die einzelne Seite [Default.aspx] alle ihre Komponenten instanziiert, obwohl nur einige davon zur Generierung der Antwort an den Benutzer verwendet werden. Bei jeder neuen Anfrage wird somit unnötige Arbeit verrichtet, was zu einem Engpass wird, wenn die Gesamtzahl der Komponenten auf der Seite groß ist.

Eine Lösung besteht darin, die Ansichten auf verschiedene Seiten zu verteilen. Genau das tun wir hier. Betrachten wir zwei verschiedene Fälle der Ansichtsgenerierung:

  1. Die Anfrage wird an die Seite P1 gestellt, und diese generiert die Antwort
  2. Die Anfrage wird an die Seite P1 gestellt, und diese Seite fordert die Seite P2 auf, die Antwort zu generieren

11.2.1. Fall 1: eine Controller-/Ansichtsseite

In Fall 1 kehren wir zur Single-Controller-Architektur der vorherigen Version zurück, bei der die Seite [Default.aspx] die Seite P1 ist:

  1. Der Client sendet eine Anfrage an Seite P1 (1)
  2. Seite P1 verarbeitet diese Anfrage. Dazu benötigt sie möglicherweise Unterstützung von der [Business]-Schicht (2), die ihrerseits die [DAO]-Schicht benötigt, falls Daten mit der Datenbank ausgetauscht werden müssen. Die Anwendung erhält eine Antwort von der [Business]-Schicht.
  3. Auf der Grundlage dieser Antwort wählt sie (3) die Ansicht (= die Antwort) aus, die an den Client gesendet werden soll, und stellt ihm (4) die benötigten Informationen (das Modell) zur Verfügung. Dazu gehören die Auswahl der [Panel]- oder [View]-Komponenten, die auf Seite P1 angezeigt werden sollen, sowie die Initialisierung der darin enthaltenen Komponenten.
  4. Die Antwort wird an den Client gesendet (5)

Hier sind zwei Beispiele aus der untersuchten Anwendung:

[Seite Formulaire.aspx]

  • in [1]: Nach dem Aufruf der Seite [Formulaire.aspx] fordert der Benutzer eine Simulation an
  • in [2]: Die Seite [Formulaire.aspx] hat diese Anfrage verarbeitet und die Antwort selbst generiert, indem sie eine [View]-Komponente anzeigte, die in [1] nicht angezeigt worden war

[Seite Simulations.aspx]

  • in [1]: Nach dem Aufrufen der Seite [Simulations.aspx] möchte der Benutzer eine Simulation entfernen
  • in [2]: Die Seite [Simulations.aspx] hat diese Anfrage bearbeitet und die Antwort selbst generiert, indem sie die neue Liste der Simulationen erneut anzeigt.

11.2.2. Fall 2: eine Seite mit einem einzigen Controller, eine Controller/View-Seite

Fall 2 kann verschiedene Architekturen abdecken. Wir wählen die folgende:

  1. Der Client sendet eine Anfrage an Seite P1 (1)
  2. Seite P1 verarbeitet diese Anfrage. Dazu benötigt sie möglicherweise Unterstützung von der [Business]-Schicht (2), die ihrerseits die [DAO]-Schicht benötigt, falls Daten mit der Datenbank ausgetauscht werden müssen. Die Anwendung erhält eine Antwort von der [Business]-Schicht.
  3. Auf dieser Grundlage wählt sie (3) die Ansicht (= die Antwort) aus, die an den Client gesendet werden soll, und versorgt diese (4) mit den benötigten Informationen (der Vorlage). In diesem Fall muss die zu generierende Ansicht von einer anderen Seite als P1 erstellt werden – konkret von Seite P2. Um die Vorgänge (3) und (4) auszuführen, hat Seite P1 zwei Möglichkeiten:
    • Übergeben Sie die Ausführung an Seite P2 mithilfe der Operation [Server.Transfer("P2.aspx")]. In diesem Fall kann das für Seite P2 vorgesehene Modell im Anforderungskontext [Context.Items["key"]=value] oder in der Benutzersitzung [Session.["key"]=value] abgelegt werden. Seite P2 wird dann instanziiert, und wenn beispielsweise ihr Load-Ereignis verarbeitet wird, kann sie die von Seite P1 übergebenen Informationen mithilfe der Operationen [value=(Type)Context.Items["key"] oder [value=(Type)Session["key"] abrufen, je nach Bedarf, wobei Type der Typ des mit dem Schlüssel verknüpften Werts ist. Die Übertragung von Werten über den Kontext ist am besten geeignet, wenn die Modellwerte nicht für eine zukünftige Client-Anfrage beibehalten werden müssen.
    • Weisen Sie den Client an, mithilfe der Operation [Response.Redirect("P2.aspx")] auf Seite P2 umzuleiten. In diesem Fall legt Seite P1 das für Seite P2 bestimmte Modell in der Sitzung ab, da der Anforderungskontext am Ende jeder Anforderung gelöscht wird. Hier führt die Umleitung jedoch dazu, dass die erste Client-Anforderung an P1 beendet wird und eine zweite Anforderung desselben Clients ausgelöst wird, diesmal an P2. Es gibt zwei aufeinanderfolgende Anforderungen. Wir wissen, dass die Sitzung eine Möglichkeit ist, „Speicher“ zwischen Anforderungen zu bewahren. Neben der Sitzung gibt es noch andere Lösungen.
  4. Unabhängig davon, wie P2 die Kontrolle übernimmt, kehren wir dann zu Fall 1 zurück: P2 hat eine Anfrage erhalten, die es bearbeiten wird (5), und wird die Antwort selbst generieren (6, 7). Wir können uns auch vorstellen, dass Seite P2 nach der Bearbeitung der Anfrage an Seite P3 übergibt und so weiter.

Hier ist ein Beispiel aus der untersuchten Anwendung:

  • in [1]: Der Benutzer, der die Seite [Formulaire.aspx] aufgerufen hat, fordert die Liste der Simulationen an
  • in [2]: Die Seite [Formulaire.aspx] verarbeitet diese Anfrage und leitet den Client auf die Seite [Simulations.aspx] weiter. Letztere liefert dem Benutzer die Antwort. Anstatt den Client um eine Weiterleitung zu bitten, hätte die Seite [Formulaire.aspx] die Anfrage des Clients an die Seite [Simulations.aspx] weiterleiten können. In diesem Fall in [2] hätten wir dieselbe URL wie in [1] gesehen. Tatsächlich zeigt ein Browser immer die zuletzt angeforderte URL an:
    • Die in [1] angeforderte Aktion ist für die Seite [Formulaire.aspx] bestimmt. Der Browser sendet eine POST-Anfrage an diese Seite.
    • Wenn die Seite [Formulaire.aspx] die Anfrage verarbeitet und sie dann über [Server.Transfer("Simulations.aspx")] an die Seite [Simulations.aspx] weiterleitet, bleiben wir innerhalb derselben Anfrage. Der Browser zeigt dann in [2] die URL von [Formulaire.aspx] an, an die der POST-Befehl gesendet wurde.
    • Wenn die Seite [Formulaire.aspx] die Anfrage verarbeitet und sie dann über [Response.Redirect("Simulations.aspx")] an die Seite [Simulations.aspx] weiterleitet, stellt der Browser eine zweite Anfrage, eine GET-Anfrage an [Simulations.aspx]. Der Browser zeigt dann in [2] die URL von [Simulations.aspx] an, an die die GET-Anfrage gesendet wurde. Dies zeigt uns der obige Screenshot [2].

11.3. Das Visual Web Developer-Projekt für die [web]-Schicht

Das Visual Web Developer-Projekt für die [Web]-Schicht sieht wie folgt aus:

  • In [1] finden wir:
    • die Konfigurationsdatei der Anwendung [Web.config] – die mit der der Anwendung [pam-v4-3tier-nhibernate-multivues-monopage] identisch ist.
    • die Seite [Default.aspx] – leitet den Client einfach auf die Seite [Formulaire.aspx] weiter
    • die Seite [Formulaire.aspx], die dem Benutzer das Simulationsformular anzeigt und Aktionen im Zusammenhang mit diesem Formular verarbeitet
    • die Seite [Simulations.aspx], die die Liste der Simulationen des Benutzers anzeigt und Aktionen im Zusammenhang mit dieser Seite verarbeitet
    • Die Seite [Errors.aspx], die dem Benutzer eine Seite anzeigt, auf der ein beim Start der Webanwendung aufgetretener Fehler gemeldet wird.
  • In [2] sehen wir die Projektverweise.

Kehren wir zur Architektur des neuen Projekts zurück:

Im Vergleich zum Projekt [pam-v4-3tier-nhibernate-multivues-monopage] haben sich nur die Ansichten geändert. Aus diesem Grund verwendet das neue Projekt einige der Dateien aus jenem Projekt wieder:

  • die Konfigurationsdatei [Web.config]
  • die referenzierten DLLs [pam-dao-nhibernate, pam-metier-dao-nhibernate, Spring.Core, NHibernate]
  • die globale Anwendungsklasse [Global.asax]
  • die Ordner [images, resources, pam]

Um die Konsistenz mit dem derzeit in Entwicklung befindlichen Projekt zu gewährleisten, stellen wir sicher, dass der Namespace für die Ansichten und die globale Anwendungsklasse [pam-v7] lautet:

  

11.4. Der Code für die Seitendarstellung

11.4.1. Die Master-Seite [MasterPage.master]

Die in Abschnitt 11.1 vorgestellten Anwendungsansichten weisen gemeinsame Elemente auf, die in eine Master-Seite integriert werden können, die in Visual Studio als Master-Seite bezeichnet wird. Nehmen wir zum Beispiel die folgenden Ansichten [VueSaisies] und [VueSimulationsVides], die jeweils von den Seiten [Formulaire.aspx] und [Simulations.aspx] generiert werden:

Diese beiden Ansichten haben das obere Banner (Titel und Menüoptionen) gemeinsam. Dies gilt für alle Ansichten, die dem Benutzer angezeigt werden: Sie alle werden dasselbe obere Banner aufweisen. Um zu ermöglichen, dass verschiedene Seiten dasselbe Präsentationsfragment gemeinsam nutzen, gibt es verschiedene Lösungen, darunter die folgende:

  • Platzieren Sie dieses gemeinsame Fragment in einem Benutzersteuerelement. Dies war die primäre Technik in ASP.NET 1.1
  • Platzieren Sie dieses gemeinsame Fragment in einer Master-Seite. Diese Technik wurde mit ASP.NET 2.0 eingeführt. Diese verwenden wir hier.

Um eine Masterseite in einer Webanwendung zu erstellen, gehen Sie wie folgt vor:

  • Klicken Sie mit der rechten Maustaste auf das Projekt / Neues Element hinzufügen / Master-Seite:

Durch das Hinzufügen einer Master-Seite werden standardmäßig drei Dateien zur Webanwendung hinzugefügt:

  • [MasterPage.master]: der Layout-Code für die Master-Seite
  • [MasterPage.master.cs]: der Steuerelementcode für die Master-Seite
  • [MasterPage.Master.Designer.cs]: die Komponentendeklarationen für die Master-Seite

Der von Visual Studio in [MasterPage.master] generierte Code lautet wie folgt:


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MasterPage.master.cs" Inherits="pam_v7.MasterPage" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <asp:ContentPlaceHolder id="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
 
        </asp:ContentPlaceHolder>
    </div>
    </form>
</body>
</html>
  • Zeile 1: Mit dem Tag <%@ Master ... %> wird die Seite als Master-Seite definiert. Der Steuerungscode der Seite befindet sich in der durch das CodeBehind-Attribut angegebenen Datei, und die Seite erbt von der durch das Inherits-Attribut angegebenen Klasse.
  • Zeilen 12–18: Das Formular der Master-Seite
  • Zeilen 14–16: Ein leerer Container, der in unserer Anwendung eine der Seiten [Form.aspx, Simulations.aspx, Errors.aspx] enthält. Der Client erhält als Antwort immer dieselbe Seite – die Master-Seite –, in der der Container [ContentPlaceHolder1] einen HTML-Stream empfängt, der von einer der Seiten [Form.aspx, Simulations.aspx, Errors.aspx] bereitgestellt wird. Um das Erscheinungsbild der an Clients gesendeten Seiten zu ändern, müssen Sie also lediglich das Erscheinungsbild der Master-Seite ändern.
  • Zeilen 8–9: Ein leerer Container, der es den „untergeordneten“ Seiten ermöglicht, die Kopfzeile <head>...</head> anzupassen.

Die visuelle Darstellung (Registerkarte „Entwurf“) dieses Quellcodes ist unten in (1) zu sehen. Darüber hinaus können Sie mithilfe der Komponente [ContentPlaceHolder] (2) aus der Symbolleiste [Standard] beliebig viele Container hinzufügen.

Der von Visual Studio in [MasterPage.master.cs] generierte Steuerungscode lautet wie folgt:


using System;
 
public partial class MasterPage : System.Web.UI.MasterPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
 
    }
}
  • Zeile 3: Die Klasse, auf die das [Inherits]-Attribut der <%@ Master ... %>-Anweisung in der Seite [MasterPage.master] verweist, leitet sich von der Klasse [System.Web.UI.MasterPage] ab

Oben sehen wir die Methode Page_Load, die das Load-Ereignis der Master-Seite verarbeitet. Die Master-Seite enthält eine weitere Seite. In welcher Reihenfolge treten die Load-Ereignisse der beiden Seiten auf? Es gilt die allgemeine Regel: Das Load-Ereignis einer Komponente tritt vor dem ihres Containers auf. Hier tritt das Load-Ereignis der in die Master-Seite eingebetteten Seite daher vor dem der Master-Seite selbst auf.

Um in „ “ eine Seite zu erstellen, die die vorherige Seite [MasterPage.master] als Master-Seite verwendet, können Sie wie folgt vorgehen:

  • in [1]: Klicken Sie mit der rechten Maustaste auf die Master-Seite und wählen Sie dann [Inhaltsseite hinzufügen]
  • in [2]: Es wird eine Standardseite, in diesem Fall [WebForm1.aspx], erstellt.

Der Präsentationscode für [WebForm1.aspx] lautet wie folgt:


<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.Master" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="pam_v7.WebForm1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
</asp:Content>
  • Zeile 1: Die Page-Direktive und ihre Attribute
    • MasterPageFile: Gibt die Master-Seiten-Datei für die durch die Anweisung beschriebene Seite an. Das Symbol ~ steht für den Projektordner.
    • Die übrigen Parameter sind die Standardparameter für eine ASP-Webseite
  • Zeilen 2–3: Die <asp:Content>-Tags werden über das Attribut „ContentPlaceHolderID“ nacheinander mit den <asp:ContentPlaceHolder>-Anweisungen in der Master-Seite verknüpft. Die zwischen den Zeilen 2 und 3 oben platzierten Komponenten werden zur Laufzeit in den Container „ContentPlaceHolder1“ auf der Master-Seite eingefügt.

Durch Umbenennen der auf diese Weise generierten Seite [WebForm1.aspx] können wir die verschiedenen Seiten unter Verwendung von [MasterPage.master] als Master-Seite erstellen.

Für unsere Anwendung [SimuPaie] sieht die Master-Seite wie folgt aus:

Nr.
Typ
Name
Rolle
A
Panel (oben rosa)
Kopfzeile
Seitenkopf
B
Panel (oben gelb)
Inhalt
Seiteninhalt
1
LinkButton
LinkButtonRunSimulation
fordert die Simulationsberechnung an
2
LinkButton
LinkButtonClearSimulation
löscht das Eingabeformular
3
LinkButton
LinkButtonViewSimulations
zeigt die Liste der bereits durchgeführten Simulationen an
4
LinkButton
LinkButtonSimulationForm
kehrt zum Eingabeformular zurück
5
LinkButton
LinkButtonSaveSimulation
speichert die aktuelle Simulation in der Simulationsliste
6
LinkButton
LinkButtonEndSession
beendet die aktuelle Sitzung

Der entsprechende Quellcode lautet wie folgt:


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MasterPage.master.cs" Inherits="pam_v7.MasterPage" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>Application PAM</title>
</head>
<body background="ressources/standard.jpg">
  <form id="form1" runat="server">
  <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
  <asp:UpdatePanel runat="server" ID="UpdatePanelPam" UpdateMode="Conditional">
    <ContentTemplate>
      <asp:Panel ID="entete" runat="server" BackColor="#FFE0C0">
        <table>
          <tr>
            <td>
              <h2>
                Simulateur de calcul de paie</h2>
            </td>
            <td>
              <label>
                &nbsp;&nbsp;&nbsp</label>
              <asp:UpdateProgress ID="UpdateProgress1" runat="server">
                <ProgressTemplate>
                  <img alt="" src="images/indicator.gif" />
                  <asp:Label ID="Label5" runat="server" BackColor="#FF8000"
                             EnableViewState="False" Text="Calcul en cours. Patientez ....">
                        </asp:Label>
                </ProgressTemplate>
              </asp:UpdateProgress>
            </td>
            <td>
              <asp:LinkButton ID="LinkButtonFaireSimulation" runat="server"
                         CausesValidation="False">| Faire la simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonEffacerSimulation" runat="server"
                         CausesValidation="False">| Effacer la simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonVoirSimulations" runat="server"
                     CausesValidation="False">| Voir les simulations<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonFormulaireSimulation" runat="server"
                         CausesValidation="False">| Retour au formulaire de simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonEnregistrerSimulation" runat="server"
                         CausesValidation="False">| Enregistrer la simulation<br />
                    </asp:LinkButton>
              <asp:LinkButton ID="LinkButtonTerminerSession" runat="server"
                         CausesValidation="False">| Terminer la session<br />
                    </asp:LinkButton>
            </td>
          </tr>
        </table>
        <hr />
      </asp:Panel>
      <div>
        <asp:Panel ID="contenu" runat="server" BackColor="#FFFFC0">
          <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
          </asp:ContentPlaceHolder>
        </asp:Panel>
      </div>
    </ContentTemplate>
  </asp:UpdatePanel>
  </form>
</body>
</html>
  • Zeile 1: Beachten Sie den Namen der Master-Seitenklasse: MasterPage
  • Zeile 8: Definieren Sie ein Hintergrundbild für die Seite.
  • Zeilen 9–64: das Formular
  • Zeile 10: Die für Ajax-Effekte erforderliche ScriptManager-Komponente
  • Zeilen 11–63: der AJAX-Container
  • Zeilen 12–62: der Ajax-fähige Inhalt
  • Zeilen 13–55: die Panel-Komponente [Kopfzeile]
  • Zeilen 57–60: die Panel-Komponente [content]
  • Zeilen 58–59: die [ContentPlaceHolder1]-Komponente, die die gekapselte Seite [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] enthält

Um diese Seite zu erstellen, können wir in das [header]-Panel den ASPX-Code der Ansicht [HeaderView] aus der Seite [Default.aspx] der Version [pam-v4-3tier-nhibernate-multivues-monopage] einfügen, die in Abschnitt 8.5.2 beschrieben wird.

11.4.2. Die Seite [Formulaire.aspx]

Um diese Seite zu generieren, befolgen Sie die in Abschnitt 11.4.1 beschriebene Vorgehensweise und benennen Sie die generierte Seite [WebForm1.aspx] in [Form.aspx] um. Das Erscheinungsbild der im Aufbau befindlichen Seite [Form.aspx] sieht wie folgt aus:

Das Erscheinungsbild der Seite [Formulaire.aspx] besteht aus zwei Elementen:

  • [1] die Master-Seite mit ihrem [ContentPlaceHolder1]-Container (2)
  • [2] die im Container [ContentPlaceHolder1] platzierten Komponenten. Diese sind identisch mit denen in der vorherigen Anwendung.

Der Quellcode für diese Seite lautet wie folgt:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Formulaire.aspx.cs" Inherits="pam_v7.PageFormulaire" Title="Simulation de calcul de paie : formulaire" %>
 
<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
  <div>
    <table>
      <tr>
        <td>
          Employé
        </td>
        <td>
          Heures travaillées
        </td>
        <td>
          Jours travaillés
        </td>
        <td>
        </td>
      </tr>
...
</asp:Content>
  • Zeile 1: Die Page-Anweisung mit ihrem MasterPageFile-Attribut
  • Zeile 4: Die Master-Page-Steuerungsklasse kann öffentliche Felder und Eigenschaften offenlegen. Auf diese kann von den gekapselten Seiten aus mit der Syntax Master.[Feld] oder Master.[Eigenschaft] zugegriffen werden. Die Master-Eigenschaft der Seite bezieht sich auf die Master-Page als Instanz des Typs [System.Web.UI.MasterPage]. Daher sollten wir in unserem Beispiel eigentlich (MasterPage)(Master).[Feld] oder (MasterPage)(Master).[property]. Diese Typumwandlung lässt sich vermeiden, indem die MasterType-Anweisung aus Zeile 4 in die Seite eingefügt wird. Das VirtualPath-Attribut dieser Anweisung gibt die Master-Page-Datei an. Der Compiler kann dann die öffentlichen Felder, Eigenschaften und Methoden erkennen, die von der Master-Page-Klasse bereitgestellt werden, in diesem Fall vom Typ [MasterPage].
  • Zeilen 5–22: Der Inhalt, der in den Container [ContentPlaceHolder1] der Master-Seite eingefügt werden soll.

Diese Seite kann erstellt werden, indem ihr Inhalt (Zeilen 6–21) auf den der in Abschnitt 8.5.3 beschriebenen Ansicht [VueSaisies] und den der in Abschnitt 8.5.4 beschriebenen Ansicht [VueSimulation] gesetzt wird.

11.4.3. Die Seite [Simulations.aspx]

Um diese Seite zu generieren, befolgen Sie die in Abschnitt 11.4.1 beschriebene Vorgehensweise und benennen Sie die resultierende Seite [WebForm1.aspx] in [Simulations.aspx] um. Das visuelle Erscheinungsbild der derzeit im Aufbau befindlichen Seite [Simulations.aspx] sieht wie folgt aus:

Das Erscheinungsbild der Seite [Simulations.aspx] besteht aus zwei Elementen:

  • [1] die Master-Seite mit ihrem [ContentPlaceHolder1]-Container
  • unter [2] die Komponenten, die im Container [ContentPlaceHolder1] platziert wurden. Diese sind identisch mit denen in der vorherigen Anwendung.

Der Quellcode für diese Seite lautet wie folgt:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Simulations.aspx.cs" Inherits="pam_v7.PageSimulations" Title="Pam : liste des simulations" %>
 
<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
  <asp:MultiView ID="MultiView1" runat="server">
    <asp:View ID="View1" runat="server">
      <h2>
        Liste de vos simulations</h2>
      <p>
        <asp:GridView ID="GridViewSimulations" runat="server" ...>
...
        </asp:GridView>
      </p>
    </asp:View>
    <asp:View ID="View2" runat="server">
      <h2>
        La liste de vos simulations est vide</h2>
    </asp:View>
  </asp:MultiView><br />
</asp:Content>

Wir können diese Seite erstellen, indem wir ihren Inhalt (Zeilen 5–21) auf den der in Abschnitt 8.5.5 beschriebenen Ansicht [VueSimulations] und den der in Abschnitt 8.5.6 beschriebenen Ansicht [VueSimulationsVides] setzen.

11.4.4. Die Seite [Errors.aspx]

Um diese Seite zu generieren, befolgen Sie die in Abschnitt 11.4.1 beschriebene Vorgehensweise und benennen Sie die resultierende Seite [WebForm1.aspx] in [Errors.aspx] um. Das visuelle Erscheinungsbild der derzeit im Aufbau befindlichen Seite [Errors.aspx] sieht wie folgt aus:

Das Erscheinungsbild der Seite [Errors.aspx] besteht aus zwei Elementen:

  • [1] die Master-Seite mit ihrem [ContentPlaceHolder1]-Container
  • [2] die im [ContentPlaceHolder1]-Container platzierten Komponenten. Diese sind identisch mit denen in der vorherigen Anwendung.

Der Quellcode für diese Seite lautet wie folgt:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Erreurs.aspx.cs" Inherits="pam_v7.PageErreurs" Title="Pam : erreurs" %>
 
<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
        <h3>Les erreurs suivantes se sont produites au démarrage de l'application</h3>
        <ul>
            <asp:Repeater id="rptErreurs" runat="server">
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
            </asp:Repeater>
        </ul>
</asp:Content>

11.5. Code für das Seitensteuerelement

11.5.1. Übersicht

Kehren wir zur Anwendungsarchitektur zurück:

  • [Global] ist das [HttpApplication]-Objekt, das die Anwendung initialisiert (Schritt 0). Diese Klasse ist identisch mit der in der vorherigen Version.
  • Der Controller-Code, der in der vorherigen Version vollständig in [Default.aspx.cs] enthalten war, verteilt sich nun auf mehrere Seiten:
    • [MasterPage.master]: die Master-Seite für [Form.aspx, Simulations.aspx, Errors.aspx]. Sie enthält das Menü.
    • [Formulaire.aspx]: die Seite, die das Simulationsformular anzeigt und die auf diesem Formular ausgeführten Aktionen verarbeitet
    • [Simulations.aspx]: die Seite, die die Liste der Simulationen anzeigt und die auf dieser Seite stattfindenden Aktionen verarbeitet
    • [Errors.aspx]: die Seite, die angezeigt wird, wenn ein Fehler bei der Initialisierung der Anwendung auftritt. Auf dieser Seite sind keine Aktionen möglich.

Die Verarbeitung einer Client-Anfrage erfolgt in folgenden Schritten:

  1. Der Kunde stellt eine Anfrage an die Anwendung. Dies geschieht in der Regel auf einer der beiden Seiten [Form.aspx, Simulations.aspx], aber nichts hindert ihn daran, die Seite [Errors.aspx] anzufordern. Dieses Szenario muss berücksichtigt werden.
  2. Die angeforderte Seite verarbeitet diese Anfrage (Schritt 1). Dazu benötigt sie möglicherweise Unterstützung durch die [Business]-Schicht (Schritt 2), die ihrerseits die [DAO]-Schicht benötigt, falls Daten mit der Datenbank ausgetauscht werden müssen. Die Anwendung erhält eine Antwort von der [Business]-Schicht.
  3. Auf dieser Grundlage wählt sie (Schritt 3) die Ansicht (= die Antwort) aus, die an den Client gesendet werden soll, und versorgt sie (Schritt 4) mit den benötigten Informationen (dem Modell). Wir haben drei Möglichkeiten zur Generierung dieser Antwort gesehen:
    • Die angeforderte Seite (D) ist gleichzeitig die Seite (R), die als Antwort gesendet wird. Das Erstellen des Antwortmodells (R) besteht dann darin, bestimmten Komponenten der Seite (D) die Werte zuzuweisen, die sie in der Antwort haben müssen.
    • Die angeforderte Seite (D) ist nicht die Seite (R), die als Antwort gesendet wird. Die Seite (D) kann dann:
      • den Ausführungsfluss mithilfe der Anweisung Server.Transfer(" R ") an die Seite (R) übergeben. Das Modell kann dann mit Context.Items("key")=value in den Kontext oder, seltener, mit Session.Items("key")=value in die Sitzung gestellt werden
      • den Client mithilfe der Anweisung Response.redirect(" R ") auf Seite (R) umleiten. Das Modell kann dann in die Sitzung, jedoch nicht in den Kontext gesetzt werden.
  4. Die Antwort wird an den Client gesendet (Schritt 5)

Jede der Seiten [MasterPage.master, Form.aspx, Simulations.aspx, Errors.aspx] reagiert auf eines oder mehrere der folgenden Ereignisse:

  • Init: erstes Ereignis im Lebenszyklus der Seite
  • Load: tritt auf, wenn die Seite geladen wird
  • Click: Ein Klick auf einen der Links im Menü der Master-Seite

Wir verarbeiten die Seiten nacheinander, beginnend mit der Master-Seite.

11.5.2. Steuerungscode für die Seite [MasterPage.master]

11.5.2.1. Klassen-Skelett

Der Steuerungscode der Master-Seite hat das folgende Gerüst:


using System.Web.UI.WebControls;
 
namespace pam_v7
{
  public partial class MasterPage : System.Web.UI.MasterPage
  {
 
    // the menu 
    public LinkButton OptionFaireSimulation
    {
      get { return LinkButtonFaireSimulation; }
    }
...
 
    // set menu 
    public void SetMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
    {
....
    }
 
    // managing the [End session] option 
    protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
    {
....
    }
 
    // init master page 
    protected void Page_Init(object sender, System.EventArgs e)
    {
....
      }
    }
  }
}
  • Zeile 5: Die Klasse heißt [MasterPage] und leitet sich von der Systemklasse [System.Web.UI.MasterPage] ab.
  • Zeilen 9–14: Die 6 Menüoptionen werden als öffentliche Eigenschaften der Klasse bereitgestellt
  • Zeilen 16–19: Die öffentliche Methode SetMenu ermöglicht es den Seiten [Formulaire.aspx, Simulations.aspx, Erreurs.aspx], das Menü der Master-Seite festzulegen
  • Zeilen 22–25: Die Prozedur, die den Klick auf den Link [LinkButtonTerminerSession] verarbeitet
  • Zeilen 28–31: Die Prozedur, die das Init-Ereignis der Master-Seite verarbeitet

11.5.2.2. Öffentliche Eigenschaften der Klasse


using System.Web.UI.WebControls;
 
namespace pam_v7
{
  public partial class MasterPage : System.Web.UI.MasterPage
  {
 
    // the menu 
    public LinkButton OptionFaireSimulation
    {
      get { return LinkButtonFaireSimulation; }
    }
 
    public LinkButton OptionEffacerSimulation
    {
      get { return LinkButtonEffacerSimulation; }
    }
 
    public LinkButton OptionEnregistrerSimulation
    {
      get { return LinkButtonEnregistrerSimulation; }
    }
 
    public LinkButton OptionVoirSimulations
    {
      get { return LinkButtonVoirSimulations; }
    }
 
    public LinkButton OptionTerminerSession
    {
      get { return LinkButtonTerminerSession; }
    }
 
    public LinkButton OptionFormulaireSimulation
    {
      get { return LinkButtonFormulaireSimulation; }
    }
 
...
  }
}

Um diesen Code zu verstehen, müssen Sie sich die Komponenten in Erinnerung rufen, aus denen die Master-Seite besteht:

Nr.
Typ
Name
Rolle
A
Panel (oben rosa)
Kopfzeile
Seitenkopf
B
Panel (oben gelb)
Inhalt
Seiteninhalt
1
LinkButton
LinkButtonSimulationStarten
Simulationsberechnung anfordern
2
LinkButton
LinkButtonClearSimulation
löscht das Eingabeformular
3
LinkButton
LinkButtonViewSimulations
zeigt die Liste der bereits durchgeführten Simulationen an
4
LinkButton
LinkButtonSimulationForm
kehrt zum Eingabeformular zurück
5
LinkButton
LinkButtonSaveSimulation
speichert die aktuelle Simulation in der Simulationsliste
6
LinkButton
LinkButtonEndSession
beendet die aktuelle Sitzung

Die Komponenten 1 bis 6 sind außerhalb der Seite, die sie enthält, nicht zugänglich. Die Eigenschaften in den Zeilen 9 bis 37 dienen dazu, sie für externe Klassen zugänglich zu machen, in diesem Fall für die Klassen der anderen Seiten der Anwendung.

11.5.2.3. Die SetMenu-Methode

Die öffentliche Methode SetMenu ermöglicht es den Seiten [Formulaire.aspx, Simulations.aspx, Erreurs.aspx], das Menü der Master-Seite festzulegen. Ihr Code ist einfach:


        // fixer le menu 
        public void SetMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
        {
            // on fixe les options de menu 
            LinkButtonFaireSimulation.Visible = boolFaireSimulation;
            LinkButtonEnregistrerSimulation.Visible = boolEnregistrerSimulation;
            LinkButtonEffacerSimulation.Visible = boolEffacerSimulation;
            LinkButtonVoirSimulations.Visible = boolVoirSimulations;
            LinkButtonFormulaireSimulation.Visible = boolFormulaireSimulation;
            LinkButtonTerminerSession.Visible = boolTerminerSession;
}

11.5.2.4. Ereignisbehandlung auf der Master-Seite

Die Master-Seite verarbeitet zwei Ereignisse:

  • das Init-Ereignis, das erste Ereignis im Lebenszyklus der Seite
  • das Click-Ereignis auf dem Link [LinkButtonTerminerSession]

Die Master-Seite enthält fünf weitere Links: [LinkButtonRunSimulation, LinkButtonSaveSimulation, LinkButtonClearSimulation, LinkButtonViewSimulations, LinkButtonSimulationForm]. Betrachten wir als Beispiel, was zu tun ist, wenn auf den Link [LinkButtonRunSimulation] geklickt wird:

  1. Überprüfen der auf der Seite [Form.aspx] eingegebenen Daten (Stunden, Tage)
  2. Berechnung des Gehalts
  3. Zeigen Sie die Ergebnisse auf der Seite [Form.aspx] an

Die Schritte 1 und 3 erfordern Zugriff auf die Komponenten auf der Seite [Form.aspx]. Dies ist jedoch nicht der Fall. Tatsächlich hat die Master-Seite keine Kenntnis von den Komponenten auf den Seiten, die in ihren [ContentPlaceHolder1]-Container eingefügt werden können. In unserem Beispiel ist es Aufgabe der Seite [Formulaire.aspx], den Klick auf den Link [LinkButtonFaireSimulation] zu verarbeiten, da diese Seite angezeigt wird, wenn dieses Ereignis eintritt. Wie kann sie über dieses Ereignis benachrichtigt werden?

  • Da der Link [LinkButtonFaireSimulation] nicht Teil der Seite [Formulaire.aspx] ist, können wir die übliche Prozedur nicht in [Formulaire.aspx] schreiben:

    private void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
    {
...
}

Sie können das Problem mit dem folgenden Code in [Formulaire.aspx] umgehen:


using System.Collections.Generic;
...
 
namespace pam_v7
{
    public partial class Formulaire : System.Web.UI.Page
    {
// page loading 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // event manager
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
...
    }
 
    // payroll calculation 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
....
    }
 
    // delete simulation 
    private void OptEffacerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 
    protected void OptVoirSimulations_Click(object sender, System.EventArgs e)
    {
...
    }
 
    protected void OptEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 }
}
  • Zeilen 12–15: Wenn das Load-Ereignis der Seite [Formulaire.aspx] eintritt, wurde die [MasterPage]-Klasse der Master-Seite instanziiert. Ihre öffentlichen Optionxx-Eigenschaften sind zugänglich und vom Typ LinkButton, einer Komponente, die das Click-Ereignis unterstützt. Wir ordnen diesen Click-Ereignissen die folgenden Methoden zu:
    • OptFaireSimulation_Click für das Click-Ereignis des LinkButtonFaireSimulation-Links
    • OptEffacerSimulation_Click für das Click-Ereignis des Links LinkButtonEffacerSimulation
    • OptVoirSimulations_Click für das Click-Ereignis des Links LinkButtonVoirSimulations
    • OptEnregistrerSimulation_Click für das Click-Ereignis auf dem Link „LinkButtonEnregistrerSimulation“

Die Verarbeitung von Klickereignissen auf die sechs Menü-Links wird wie folgt verteilt:

  • Die Seite [Formulaire.aspx] verarbeitet die Links [LinkButtonRunSimulation, LinkButtonSaveSimulation, LinkButtonClearSimulation, LinkButtonViewSimulations]
  • Die Seite [Simulations.aspx] verarbeitet den Link [LinkButtonSimulationForm]
  • Die Master-Seite [MasterPage.master] verarbeitet den Link [LinkButtonEndSession]. Für dieses Ereignis muss sie nicht wissen, welche Seite sie einbindet.

11.5.2.5. Das Init-Ereignis der Master-Seite

Die drei Seiten [Form.aspx, Simulations.aspx, Errors.aspx] der Anwendung verwenden [MasterPage.master] als ihre Master-Seite. Nennen wir die Master-Seite M und die eingeschlossene Seite E. Wenn Seite E vom Client angefordert wird, treten die folgenden Ereignisse in dieser Reihenfolge auf:

  • E.Init
  • M.Init
  • E.Load
  • M.Load
  • ...

Wir werden das Init-Ereignis der Seite M nutzen, um Code auszuführen, der so früh wie möglich ausgeführt werden soll, unabhängig von der Zielseite E. Um diesen Code zu identifizieren, sehen wir uns die Anwendungsübersicht an:

Oben ist [Global] das [HttpApplication]-Objekt, das die Anwendung initialisiert. Diese Klasse ist dieselbe wie in der Version [pam-v4-3tier-nhibernate-multivues-monopage]:


using System;
...
 
namespace pam_v7
{
  public class Global : System.Web.HttpApplication
  {
    // --- static application data ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;
 
    // application startup
    public void Application_Start(object sender, EventArgs e)
    {
...
    }
 
    public void Session_Start(object sender, EventArgs e)
    {
...
    }
  }
}

Wenn die Klasse [Global] die Anwendung nicht korrekt initialisieren kann, legt sie zwei statische öffentliche Variablen fest:

  • Die boolesche Variable `Error` in Zeile 12 wird auf `true` gesetzt
  • die Variable `Msg` in Zeile 11 enthält eine Meldung mit Details zum aufgetretenen Fehler

Wenn ein Benutzer eine der Seiten [Form.aspx, Simulations.aspx] aufruft, während die Anwendung noch nicht korrekt initialisiert wurde, muss diese Anfrage an die Seite [Errors.aspx] weitergeleitet oder umgeleitet werden, auf der die Fehlermeldung aus der Klasse [Global] angezeigt wird. Es gibt mehrere Möglichkeiten, mit dieser Situation umzugehen:

  • Führen Sie die Überprüfung auf Initialisierungsfehler im Init- oder Load-Ereignishandler jeder der Seiten [Formulaire.aspx, Simulations.aspx] durch
  • Führen Sie die Initialisierungsfehlerprüfung im Init- oder Load-Ereignishandler der Master-Seite für diese beiden Seiten durch. Diese Methode hat den Vorteil, dass die Initialisierungsfehlerprüfung an einer einzigen Stelle platziert wird.

Wir entscheiden uns dafür, die Initialisierungsfehlerprüfung im Init-Ereignishandler der Master-Seite durchzuführen:


        protected void Page_Init(object sender, System.EventArgs e)
        {
            // event manager 
            LinkButtonTerminerSession.Click += LinkButtonTerminerSession_Click;
            // initialization errors? 
            if (Global.Erreur)
            {
                // is the encapsulated page the error page? 
                bool isPageErreurs =...;
                // if the error page is displayed, leave it alone, otherwise redirect the client to the error page 
                if (!isPageErreurs)
                    Response.Redirect("Erreurs.aspx");
                return;
            }
}

Der obige Code wird ausgeführt, sobald eine der Seiten [Form.aspx, Simulations.aspx, Errors.aspx] angefordert wird. Wenn die angeforderte Seite [Form.aspx] oder [Simulations.aspx] ist, leiten wir den Client einfach (Zeile 12) auf die Seite [Errors.aspx] um, die die Fehlermeldung aus der Klasse [Global] anzeigt. Wenn die angeforderte Seite [Errors.aspx] ist, darf diese Umleitung nicht stattfinden: Die Seite [Errors.aspx] muss angezeigt werden. Wir müssen daher innerhalb der [Page_Init]-Methode der Master-Seite wissen, welche Seite sie einbindet.

Werfen wir noch einmal einen Blick auf den Komponentenbaum der Master-Seite:


...
<body background="ressources/standard.jpg">
    <form id="form1" runat="server">
        <asp:Panel ID="entete" runat="server" BackColor="#FFE0C0" Width="1239px" >
...
        </asp:Panel>
        <div>
            <asp:Panel ID="contenu" runat="server" BackColor="#FFFFC0">
                <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
                </asp:ContentPlaceHolder>
            </asp:Panel>
        </div>
    </form>
</body>
</html>
  • Zeilen 1–13: der Container mit der ID „form1“
  • Zeilen 4–6: der Container mit der ID „entete“, enthalten im Container mit der ID „form1“
  • Zeilen 8–11: der Container mit der ID „content“, enthalten im Container mit der ID „form1“
  • Zeilen 9–10: der Container mit der ID „ContentPlaceHolder1“, enthalten im Container mit der ID „content“

Eine in die Masterseite M eingebettete Seite E befindet sich im Container mit der ID „ContentPlaceHolder1“. Um auf eine Komponente mit der ID C auf dieser Seite E zu verweisen, würden wir schreiben:


this.FindControl("form1").FindControl("contenu").FindControl("ContentPlaceHolder1").FindControl("C");

Der Komponentenbaum für die Seite [Errors.aspx] sieht wie folgt aus:


<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true"
  CodeBehind="Erreurs.aspx.cs" Inherits="pam_v7.PageErreurs" Title="Pam : erreurs" %>
 
<%@ MasterType VirtualPath="~/MasterPage.master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
        <h3>Les erreurs suivantes se sont produites au démarrage de l'application</h3>
        <ul>
            <asp:Repeater id="rptErreurs" runat="server">
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
            </asp:Repeater>
        </ul>
</asp:Content>

Wenn die Seite [Errors.aspx] mit der Master-Seite M zusammengeführt wird, wird der Inhalt des obigen <asp:Content>-Tags (Zeilen 5–16) in das <asp:ContentPlaceHolder>-Tag mit der ID „ContentPlaceholder1“ auf der Seite M integriert, was zu folgendem Komponentenbaum führt:

...
<body background="ressources/standard.jpg">
    <form id="form1" runat="server">
        <asp:panel ID="entete" runat="server" BackColor="#FFE0C0" Width="1239px" >
...
        </asp:Panel>
        <div>
            <asp:Panel ID="contenu" runat="server" BackColor="#FFFFC0">
                <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
              <h3>Les erreurs suivantes se sont produites au démarrage de l'application</h3>
              <ul>
                  <asp:Repeater id="rptErreurs" runat="server">
                      <ItemTemplate>
                          <li>
                              <%# Container.DataItem %>
                          </li>
                      </ItemTemplate>
                  </asp:Repeater>
              </ul>
                </asp:ContentPlaceHolder>
            </asp:Panel>
        </div>
    </form>
</body>
</html>
  • Zeile 12: Mit der Komponente [rptErrors] lässt sich feststellen, ob die Masterseite M die Seite [Errors.aspx] enthält. Diese Komponente ist nur auf dieser Seite vorhanden.

Diese Erläuterungen reichen aus, um den Code der Prozedur [Page_Init] in der Master-Seite zu verstehen:


protected void Page_Init(object sender, System.EventArgs e)
        {
            // event manager 
            LinkButtonTerminerSession.Click += LinkButtonTerminerSession_Click;
            // initialization errors? 
            if (Global.Erreur)
            {
                // is the encapsulated page the error page? 
                bool isPageErreurs = this.FindControl("form1").FindControl("contenu").FindControl("ContentPlaceHolder1").FindControl("rptErreurs") != null;
                // if the error page is displayed, leave it alone, otherwise redirect the client to the error page 
                if (!isPageErreurs)
                    Response.Redirect("Erreurs.aspx");
                return;
            }
        }
  • Zeile 4: Wir ordnen dem Click-Ereignis des Links „LinkButtonTerminerSession“ einen Ereignis-Handler zu. Dieser Handler befindet sich in der MasterPage-Klasse.
  • Zeile 6: Wir prüfen, ob die Klasse [Global] ihren booleschen Wert „Error“ gesetzt hat
  • Zeile 9: Wenn ja, gibt der boolesche Wert „IsPageErrors“ an, ob die in der Master-Seite gekapselte Seite die Seite [Errors.aspx] ist
  • Zeile 12: Ist die in der Master-Seite eingebettete Seite nicht die Seite [Errors.aspx], wird der Client auf diese Seite umgeleitet; andernfalls geschieht nichts.

Wenn der Benutzer auf den Link [End Session] in Ansicht (1) oben klickt, muss der Inhalt der Sitzung gelöscht und ein leeres Formular (2) angezeigt werden.

Der Code für den Handler dieses Ereignisses könnte wie folgt aussehen:


        protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
        {
            // quit session 
            Session.Abandon();
            // the [form] view is displayed 
            Response.Redirect("Formulaire.aspx");
}
  • Zeile 4: Die aktuelle Sitzung wird abgebrochen
  • Zeile 6: Der Client wird auf die Seite [Form.aspx] weitergeleitet

Wir sehen, dass dieser Code keine der Komponenten der Seiten [Form.aspx, Simulations.aspx, Errors.aspx] einbezieht. Das Ereignis kann daher von der Master-Seite selbst behandelt werden.

11.5.3. Steuerungscode für die Seite [Errors.aspx]

Der Steuerungscode für die Seite [Errors.aspx] könnte wie folgt aussehen:


using System.Collections.Generic;
 
namespace pam_v7
{
  public partial class Erreurs : System.Web.UI.Page
  {
    protected void Page_Load(object sender, System.EventArgs e)
    {
      // initialization errors? 
      if (Global.Erreur)
      {
        // prepare the template for the [errors] page 
        List<string> erreursInitialisation = new List<string>();
        erreursInitialisation.Add(Global.Msg);
        // associate the error list with its component 
        rptErreurs.DataSource = erreursInitialisation;
        rptErreurs.DataBind();
      }
      // set the menu 
      Master.SetMenu(false, false, false, false, false, false);
    }
  }
}

Beachten Sie, dass die Seite [Errors.aspx] ausschließlich dazu dient, einen Fehler bei der Anwendungsinitialisierung anzuzeigen, falls ein solcher auftritt:

  • Zeile 10: Wir prüfen, ob die Initialisierung mit einem Fehler beendet wurde
  • Zeilen 13–14: Ist dies der Fall, wird die Fehlermeldung (Global.Msg) in eine Liste [InitializationErrors] aufgenommen
  • Zeilen 16–17: Die Komponente [rptErrors] wird angewiesen, diese Liste anzuzeigen
  • Zeile 20: In allen Fällen (unabhängig davon, ob ein Fehler vorliegt oder nicht) werden die Menüoptionen der Master-Seite nicht angezeigt, sodass der Benutzer von dieser Seite aus keine neuen Aktionen auslösen kann.

Was passiert, wenn der Benutzer die Seite [Errors.aspx] direkt aufruft (was er bei normaler Nutzung der Anwendung eigentlich nicht tun sollte)? Bei der Untersuchung des Codes in [MasterPage.master.cs] und [Errors.aspx.cs] stellen wir Folgendes fest:

  • bei einem Initialisierungsfehler dieser angezeigt wird
  • Wenn kein Initialisierungsfehler vorliegt, erhält der Benutzer eine Seite, die nur die Kopfzeile aus [MasterPage.master] enthält, ohne dass Menüoptionen angezeigt werden.

11.5.4. Steuerungscode für die Seite [Formulaire.aspx]

11.5.4.1. Klassen-Skelett

Das Gerüst des Steuerelementcodes für die Seite [Form.aspx] könnte wie folgt aussehen:


using Pam.Metier.Entites;
...
 
partial class PageFormulaire : System.Web.UI.Page
{
 
    // page loading 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // event manager
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
....
    }
 
    // payroll calculation 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
....
    }
 
    // delete simulation 
    private void OptEffacerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 
    protected void OptVoirSimulations_Click(object sender, System.EventArgs e)
    {
....
    }

    protected void OptEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 
}

Der Steuerungscode für die Seite [Formulaire.aspx] verarbeitet fünf Ereignisse:

  1. das Load-Ereignis der Seite
  2. das Click-Ereignis des Links [LinkButtonRunSimulation] auf der Master-Seite
  3. das Click-Ereignis des Links [LinkButtonClearSimulation] auf der Master-Seite
  4. das Klick-Ereignis des Links [LinkButtonEnregistrerSimulation] auf der Master-Seite
  5. das Click-Ereignis für den Link [LinkButtonViewSimulations] auf der Master-Seite

11.5.4.2. Ereignis „Page Load“

Das Grundgerüst des Handlers für das Page-Load-Ereignis könnte wie folgt aussehen:


    protected void Page_Load(object sender, System.EventArgs e)
    {
        // event manager
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
        // view [entries] display 
        ...
        // menu positioning master page 
        ...
        // query processing GET 
        if (!IsPostBack)
        {
            // loading employee names into the combo 
...
            // init view [entries] with entries stored in the session if they exist 
....
        }
}

Ein Beispiel zur Verdeutlichung des Kommentars in Zeile 17 könnte wie folgt lauten:

  • In [1] fordern wir die Liste der Simulationen an. Es wurden Einträge in [A, B, C] vorgenommen.
  • In [2] sehen wir die Liste
  • In [3] bitten wir darum, zum Formular
  • In [4] wird das Formular genau so angezeigt, wie es verlassen wurde. Da es zwei Anfragen gab, (1,2) und (3,4), bedeutet dies:
    • beim Wechsel von [1] zu [2] die Einträge aus [1] gespeichert wurden
    • beim Wechsel von [3] nach [4] wurden sie wiederhergestellt. Es ist die Prozedur [Page_Load] in [Form.aspx], die diese Wiederherstellung durchführt.

Frage: Vervollständigen Sie die Page_Load-Prozedur unter Verwendung der Kommentare und des Codes aus der Version [pam-v4-3tier-nhibernate-multivues-monopage]


Das Grundgerüst der Klick-Ereignisbehandler für die Links auf der Master-Seite sieht wie folgt aus:


// payroll calculation 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
        // ajax effect
        Thread.Sleep(3000);
        // valid page? 
        Page.Validate();
        if (!Page.IsValid)
        {
            // view display [input] 
...
        }
        // the page is validated - inputs are retrieved 
...
        // we calculate the employee's salary 
        FeuilleSalaire feuillesalaire;
        try
        {
            feuillesalaire = ...;
        }
        catch (PamException ex)
        {
            // we encountered a problem 
...
            return;
        }
        // put the result in the session 
        Session["simulation"] = ...;
        // put the entries in the session 
...
        // display 
...
        // views display 
...
        // display menu MasterPage 
...
    }
 
    // delete simulation 
    private void OptEffacerSimulation_Click(object sender, System.EventArgs e)
    {
        // display panel [input] 
...
        // selection 1st employee 
...
    }
 
    protected void OptVoirSimulations_Click(object sender, System.EventArgs e)
    {
        // put the entries in the session 
...
        // the [simulations] view is displayed 
        Response.Redirect("simulations.aspx");
    }
 
    protected void OptEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
        // save the current simulation in the user's session 
...
        // the [simulations] view is displayed 
        Response.Redirect("simulations.aspx");
}

Frage: Vervollständigen Sie den Code für die oben genannten Prozeduren unter Verwendung der Kommentare und des Codes aus der Version [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.5. Steuerungscode für die Seite [Simulations.aspx]

Das Grundgerüst des Steuerungscodes für die Seite [Simulations.aspx] könnte wie folgt aussehen:


using System.Collections.Generic;
using Pam.Web;
using System.Web.UI.WebControls;
 
partial class PageSimulations : System.Web.UI.Page
{
 
    // simulations 
    private List<Simulation> simulations;
 
    // page loading
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // event manager 
        Master.OptionFormulaireSimulation.Click += OptFormulaireSimulation_Click;
        GridViewSimulations.RowDeleting += GridViewSimulations_RowDeleting;
        // simulations are retrieved from the
        simulations = ...;
        // are there any simulations? 
        if (simulations.Count != 0)
        {
            // first view visible 
            ...
            // fill the gridview 
            ...
        }
        else
        {
            // second view 
            ...
        }
        // set the menu 
        ...
    }
 
    protected void GridViewSimulations_RowDeleting(object sender, System.Web.UI.WebControls.GridViewDeleteEventArgs e)
    {
        // simulations are retrieved from the
        List<Simulation> simulations = ...;
        // delete the designated simulation (e.RowIndex represents the number of the deleted line in the gridview)
        ..
        // are there any simulations left? 
        if (simulations.Count != 0)
        {
            // fill the gridview 
            ...
        }
        else
        {
            // view [SimulationsVides] 
            ...
        }
    }
 
    protected void OptFormulaireSimulation_Click(object sender, System.EventArgs e)
    {
        // the [form] view is displayed 
        Response.Redirect("formulaire.aspx");
    }
 
}

Frage: Vervollständigen Sie den Code für die oben genannten Prozeduren unter Verwendung der Kommentare und des Codes aus der Version [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.6. Steuerungscode für die Seite [Default.aspx]

Sie können eine [Default.aspx]-Seite in die Anwendung einbinden, damit der Benutzer die URL der Anwendung aufrufen kann, ohne eine Seite anzugeben, wie unten gezeigt:

Die Anfrage [1] erhielt die Seite [Formulaire.aspx] (2) als Antwort. Wir wissen, dass die Anfrage (1) standardmäßig von der Seite [Default.aspx] der Anwendung bearbeitet wird. Um (2) zu erhalten, muss [Default.aspx] den Client lediglich auf die Seite [Formulaire.aspx] umleiten. Dies lässt sich mit dem folgenden Code erreichen:


partial class _Default : System.Web.UI.Page
{
 
    protected void Page_Init(object sender, System.EventArgs e)
    {
        // redirects to the input form 
        Response.Redirect("Formulaire.aspx");
    }
}

Die Präsentationsseite [Default.aspx] enthält lediglich die Anweisung, die sie mit [Default.aspx.cs] verknüpft:


<%@ Page Language="C#" AutoEventWireup="true"
  CodeBehind="Default.aspx.cs" Inherits="pam_v7._Default" Title="Untitled Page" %>