Skip to content

11. L'application [SimuPaie] – version 7 – ASP.NET / multi-vues / multi-pages


Lectures conseillées : référence [1], Développement WEB avec ASP.NET 1.1 paragraphe : Exemples


Nous étudions maintenant une version fonctionnellement identique à l'application ASP.NET à trois couches [pam-v4-3tier-nhibernate-multivues-monopage] étudiée précédemment mais nous modifions l'architecture de cette dernière de la façon suivante : là où dans la version précédente, les vues étaient implémentées par une unique page ASPX, ici elles seront implémentées par trois pages ASPX.

L'architecture de l'application précédente était la suivante :

On a ici une architecture MVC (Modèle – Vue – Contrôleur) :

  • [Default.aspx.cs] contient le code du contrôleur. La page [Default.aspx] est l'unique interlocuteur du client. Elle voit passer toutes les requêtes de celui-ci.
  • [Saisies, Simulation, Simulations, ...] sont les vues. Ces vues sont implémentées ici, par des composants [View] de la page [Default.aspx].

L'architecture de la nouvelle version sera elle la suivante :

  • seule la couche [web] évolue
  • les vues (ce qui est présenté à l'utilisateur) ne changent pas.
  • le code du contrôleur, qui était dans la version précédente, tout entier dans [Default.aspx.cs] est désormais réparti sur plusieurs pages :
    • [MasterPage.master] : une page qui factorise ce qui est commun aux différentes vues : le bandeau supérieur avec ses options de menu
    • [Formulaire.aspx] : la page qui présente le formulaire de simulation et gère les actions qui ont lieu sur ce formulaire
    • [Simulations.aspx] : la page qui présente la liste des simulations et gère les actions qui ont lieu sur cette même page
    • [Erreurs.aspx] : la page qui est affichée lors d'une erreur d'initialisation de l'application. Il n'y a pas d'actions possibles sur cette page.

On peut considérer qu'on a là une architecture MVC à contrôleurs multiples alors que l'architecture de la version précédente était une architecture MVC à contrôleur unique.

Le traitement d'une demande d'un client se déroule selon les étapes suivantes :

  1. le client fait une demande à l'application. Il la fait à l'une des deux pages [Formulaire.aspx, Simulations.aspx].
  2. la page demandée traite cette demande. Pour ce faire, elle peut avoir besoin de l'aide de la couche [métier] qui elle-même peut avoir besoin de la couche [dao] si des données doivent être échangées avec la base de données. L'application reçoit une réponse de la couche [métier].
  3. selon celle-ci, elle choisit (3) la vue (= la réponse) à envoyer au client en lui fournissant (4) les informations (le modèle) dont elle a besoin.
  4. la réponse est envoyée au client (5)

11.1. Les vues de l'application

Les différentes vues présentées à l'utilisateur sont les suivantes :

  • - la vue [VueSaisies] qui présente le formulaire de simulation

Image

  • - la vue [VueSimulation] utilisée pour afficher le résultat détaillé de la simulation :

Image

  • - la vue [VueSimulations] qui donne la liste des simulations faites par le client

Image

  • - la vue [VueSimulationsVides] qui indique que le client n'a pas ou plus de simulations :

Image

  • la vue [VueErreurs] qui indique une erreur d'initialisation de l'application :

Image

11.2. Génération des vues dans un contexte multi-contrôleurs

Dans la version précédente, toutes les vues étaient générées à partir de l'unique page [Default.aspx]. Celle-ci contenait deux composants [MultiView] et les vues étaient composées d'une réunion d'un ou deux composants [View] appartenant à ces deux composants [MultiView].

Efficace lorsqu'il y a peu de vues, cette architecture atteint ses limites dès que le nombre des composants formant les différentes vues devient important : en effet, à chaque requête qui est faite à l'unique page [Default.aspx], tous les composants de celle-ci sont instanciés alors même que seuls certains d'entre-eux vont être utilisés pour générer la réponse à l'utilisateur. Un travail inutile est alors fait à chaque nouvelle requête, travail qui devient pénalisant lorsque le nombre total de composants de la page est important.

Une solution est alors de répartir les vues sur différentes pages. C'est ce que nous faisons ici. Etudions deux cas différents de génération de vues :

  1. la requête est faite à une page P1 et celle-ci génère la réponse
  2. la requête est faite à une page P1 et celle-ci demande à une page P2 de générer la réponse

11.2.1. Cas 1 : une page contrôleur / vue

Dans le cas 1, on retombe sur l'architecture mono-contrôleur de la version précédente, où la page [Default.aspx] est la page P1 :

  1. le client fait une demande à la page P1 (1)
  2. la page P1 traite cette demande. Pour ce faire, elle peut avoir besoin de l'aide de la couche [métier] (2) qui elle-même peut avoir besoin de la couche [dao] si des données doivent être échangées avec la base de données. L'application reçoit une réponse de la couche [métier].
  3. selon celle-ci, elle choisit (3) la vue (= la réponse) à envoyer au client en lui fournissant (4) les informations (le modèle) dont elle a besoin. Il s'agit ici, de choisir dans la page P1 les composants [Panel] ou [View] à afficher et d'initialiser les composants qu'ils contiennent.
  4. la réponse est envoyée au client (5)

Voici deux exemples pris dans l'application étudiée :

[page Formulaire.aspx]

  • en [1] : l'utilisateur, après avoir demandé la page [Formulaire.aspx], demande une simulation
  • en [2] : la page [Formulaire.aspx] a traité cette demande et généré elle-même la réponse en affichant un composant [View] qui n'avait pas été affiché en [1]

[page Simulations.aspx]

  • en [1] : l'utilisateur, après avoir demandé la page [Simulations.aspx], veut retirer une simulation
  • en [2] : la page [Simulations.aspx] a traité cette demande et généré elle-même la réponse en réaffichant la nouvelle liste de simulations.

11.2.2. Cas 2 : une page 1 contrôleur, une page 2 controleur / vue

Le cas 2 peut recouvrir diverses d'architectures. Nous choisirons la suivante :

  1. le client fait une demande à la page P1 (1)
  2. la page P1 traite cette demande. Pour ce faire, elle peut avoir besoin de l'aide de la couche [métier] (2) qui elle-même peut avoir besoin de la couche [dao] si des données doivent être échangées avec la base de données. L'application reçoit une réponse de la couche [métier].
  3. selon celle-ci, elle choisit (3) la vue (= la réponse) à envoyer au client en lui fournissant (4) les informations (le modèle) dont elle a besoin. Il se trouve qu'ici, la vue à générer doit l'être par une autre page que P1, la page P2. Pour faire les opérations (3) et (4), la page P1 a deux possibilités :
    • faire un transfert d'exécution à la page P2 par l'opération [Server.Transfer(" P2.aspx ")]. Dans ce cas, elle peut mettre le modèle destiné à la page P2 dans le contexte de la requête [Context.Items[" clé "]=valeur] ou dans la session de l'utilisateur [Session.[" clé "]=valeur]. La page P2 sera alors instanciée et lors du traitement de son événement Load par exemple, elle pourra récupérer les informations transmises par la page P1 par les opérations [valeur=(Type)Context.Items[" clé "]] ou bien [valeur=(Type)Session[" clé "]] selon les cas, où Type est le type de la valeur associée à la clé. La transmission de valeurs par le contexte Context est la plus appropriée s'il n'y a pas utilité à ce que les valeurs du modèle soient conservées pour une future requête du client.
    • demander au client de se rediriger vers la page P2 par l'opération [Response.Redirect(" P2.aspx ")]. Dans ce cas, la page P1 mettra le modèle destiné à la page P2 dans la session, car le contexte Context de requête est supprimé à la fin de chaque requête. Or ici, la redirection va provoquer la fin de la 1ère requête du client vers P1 et l'émission d'une seconde requête de ce même client, vers P2 cette fois. Il y a deux requêtes successives. On sait que la session est l'un des moyens de conserver de la " mémoire " entre requêtes. Il y a d'autres solutions que la session.
  4. quelque soit la façon dont P2 prend la main, on retombe ensuite dans le cas 1 : P2 a reçu une requête qu'elle va traiter (5) et elle va générer elle-même la réponse (6, 7). On peut aussi imaginer que la page P2 va après traitement de la requête, passer la main à une page P3, et ainsi de suite.

Voici un exemple pris dans l'application étudiée :

  • en [1] : l'utilisateur qui a demandé la page [Formulaire.aspx] demande à voir la liste des simulations
  • en [2] : la page [Formulaire.aspx] traite cette demande et redirige le client vers la page [Simulations.aspx]. C'est cette dernière qui fournit la réponse à l'utilisateur. Au lieu de demander au client de se rediriger, la page [Formulaire.aspx] aurait pu transférer la demande du client vers la page [Simulations.aspx]. Dans ce cas en [2], on aurait vu la même Url qu'en [1]. En effet, un navigateur affiche toujours la dernière Url demandée :
    • l'action demandée en [1] est destinée à la page [Formulaire.aspx]. Le navigateur fait un POST vers cette page.
    • si la page [Formulaire.aspx] traite la demande puis la transfère par [Server.Transfer(" Simulations.aspx ")] à la page [Simulations.aspx], on reste dans la même requête. Le navigateur affichera alors en [2], l'URL de [Formulaire.aspx] vers qui a eu lieu le POST.
    • si la page [Formulaire.aspx] traite la demande puis la redirige par [Response.Redirect(" Simulations.aspx ")] vers la page [Simulations.aspx], le navigateur fait alors une 2ième requête, un GET vers [Simulations.aspx]. Le navigateur affichera alors en [2], l'URL de [Simulations.aspx] vers qui a eu lieu le GET. C'est ce que la copie d'écran [2] ci-dessus nous montre.

11.3. Le projet Visual Web Developer de la couche [web]

Le projet Visual Web Developer de la couche [web] est le suivant :

  • en [1] on trouve :
    • le fichier de configuration [Web.config] de l'application – est identique à celui de l'application [pam-v4-3tier-nhibernate-multivues-monopage].
    • la page [Default.aspx] – se contente de rediriger le client vers la page [Formulaire.aspx]
    • la page [Formulaire.aspx] qui présente à l'utilisateur le formulaire de simulation et traite les actions liées à ce formulaire
    • la page [Simulations.aspx] qui présente à l'utilisateur la liste de ses simulations et traite les actions liées à cette page
    • la page [Erreurs.aspx] qui présente à l'utilisateur une page signalant une erreur rencontrée au démarrage de l'application web.
  • en [2] on voit les références du projet.

Revenons à l'architecture du nouveau projet :

Vis à vis du projet [pam-v4-3tier-nhibernate-multivues-monopage], seules changent les vues. C'est ainsi que le nouveau projet reprend certains des fichiers de ce projet :

  • le fichier de configuration [Web.config]
  • les DLL référencées [pam-dao-nhibernate, pam-metier-dao-nhibernate, Spring.Core, NHibernate]
  • la classe globale d'application [Global.asax]
  • les dossiers [images, ressources, pam]

Pour être cohérent avec le projet en cours de construction, on fera en sorte que l'espace de noms des vues et de la classe globale d'application soit [pam-v7] :

  

11.4. Le code de présentation des pages

11.4.1. La page maître [MasterPage.master]

Les vues de l'application présentées au paragraphe 11.1, ont des parties communes qu'on peut factoriser dans une page Maître, appelée la Master Page dans Visual Studio. Prenons par exemple, les vues [VueSaisies] et [VueSimulationsVides] ci-dessous, générées respectivement par les pages [Formulaire.aspx] et [Simulations.aspx] :

Ces deux vues possèdent en commun, le bandeau supérieur (Titre et Options de menu). Il en est ainsi de toutes les vues qui seront présentées à l'utilisateur : elles auront toutes le même bandeau supérieur. Pour que différentes pages partagent un même fragment de présentation, il existe diverses solutions dont les suivantes :

  • mettre ce fragment commun dans un composant utilisateur. C'était la principale technique avec ASP.NET 1.1
  • mettre ce fragment commun dans une page Maître. Cette technique est apparue avec ASP.NET 2.0. C'est celle que nous utilisons ici.

Pour créer une page Maître dans une application web, on peut procéder ainsi :

  • clic droit sur le projet/ Ajouter un nouvel élément / Page maître :

L'ajout d'une page maître ajoute par défaut trois fichiers à l'application web :

  • [MasterPage.master] : le code de présentation de la page Maître
  • [MasterPage.master.cs] : le code de contrôle de la page Maître
  • [Masterpage.Master.designer.cs] : la déclaration des composants de la page maître

Le code généré par Visual Studio dans [MasterPage.master] est le suivant :


<%@ 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>
  • ligne 1 : la balise <%@ Master ... %> sert à définir la page comme une page Maître. Le code de contrôle de la page sera dans le fichier défini par l'attribut CodeBehind, et la page héritera de la classe définie par l'attribut Inherits.
  • lignes 12-18 : le formulaire de la page Maître
  • lignes 14-16 : un conteneur vide qui contiendra dans notre application, l'une des pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. Le client reçoit en réponse, toujours la même page, la page Maître, dans laquelle le conteneur [ContentPlaceHolder1] va recevoir un flux HTML fourni par l'une des pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. Ainsi pour changer l'aspect des pages envoyées aux clients, il suffit de changer l'aspect de la page Maître.
  • lignes 8-9 : un conteneur vide avec lequel les pages "fille" pourront personnaliser l'entête <head>...</head>.

La représentation visuelle (onglet Design) de ce code source est présentée en (1) ci-dessous. Par ailleurs, il est possible d'ajouter autant de conteneurs que souhaité, grâce au composant [ContentPlaceHolder] (2) de la barre d'outils [Standard].

Le code de contrôle généré par Visual Studio dans [MasterPage.master.cs] est le suivant :


using System;

public partial class MasterPage : System.Web.UI.MasterPage
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
}
  • ligne 3 : la classe référencée par l'attribut [Inherits] de la directive <%@ Master ... %> de la page [MasterPage.master] dérive de la classe [System.Web.UI.MasterPage]

Ci-dessus, nous voyons la présence de la méthode Page_Load qui gère l'événement Load de la page maître. La page maître contiendra en son sein une autre page. Dans quel ordre se produisent les événements Load des deux pages ? Il s'agit là d'une règle générale : l'événement Load d'un composant se produit avant celui de son conteneur. Ici, l'événement Load de la page insérée dans la page maître aura donc lieu avant celui de la page maître elle-même.

Pour générer une page qui a pour page maître la page [MasterPage.master] précédente, on pourra procéder comme suit :

  • en [1] : clic droit sur la page maître puis option [Ajouter une page de contenu]
  • en [2] : une page par défaut, ici [WebForm1.aspx] est générée.

Le code de présentation [WebForm1.aspx] est le suivant :


<%@ 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>
  • ligne 1 : la directive Page et ses attributs
    • MasterPageFile : désigne le fichier de la page maître de la page décrite par la directive. Le signe ~ désigne le dossier du projet.
    • les autres paramètres sont ceux habituels d'une page web ASP
  • lignes 2-3 : les balises <asp:Content> sont reliées une à une aux directives <asp:ContentPlaceHolder> de la page maître via l'attribut ContentPlaceHolderID. Les composants placés entre les lignes 2-3 ci-dessus, seront à l'exécution, placés dans le conteneur d'ID ContentPlaceHolder1 de la page maître.

En renommant la page [WebForm1.aspx] ainsi générée, on peut construire les différentes pages ayant [MasterPage.master] comme page maître.

Pour notre application [SimuPaie], l'aspect visuel de la page maître sera la suivante :

Type
Nom
Rôle
A
Panel (rose ci-dessus)
entete
entête de la page
B
Panel (jaune ci-dessus)
contenu
contenu de la page
1
LinkButton
LinkButtonFaireSimulation
demande le calcul de la simulation
2
LinkButton
LinkButtonEffacerSimulation
efface le formulaire de saisie
3
LinkButton
LinkButtonVoirSimulations
affiche la liste des simulations déjà faites
4
LinkButton
LinkButtonFormulaireSimulation
ramène au formulaire de saisie
5
LinkButton
LinkButtonEnregistrerSimulation
enregistre la simulation courante dans la liste des simulations
6
LinkButton
LinkButtonTerminerSession
abandonne la session courante

Le code source correspondant est le suivant :


<%@ 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>
  • ligne 1 : on notera le nom de la classe de la page maître : MasterPage
  • ligne 8 : on définit une image de fond pour la page.
  • lignes 9-64 : le formulaire
  • ligne 10 : le composant ScriptManager nécessaire aux effets Ajax
  • lignes 11-63 : le conteneur AJax
  • lignes 12-62 : le contenu ajaxifié
  • lignes 13-55 : le composant Panel [entete]
  • lignes 57-60 : le composant Panel [contenu]
  • lignes 58-59 : le composant d'ID [ContentPlaceHolder1] qui contiendra la page encapsulée [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]

Pour construire cette page, on pourra insérer dans le panel [entete], le code ASPX de la vue [VueEntete] de la page [Default.aspx] de la version [pam-v4-3tier-nhibernate-multivues-monopage], décrite au paragraphe 8.5.2.

11.4.2. La page [Formulaire.aspx]

Pour générer cette page, on suivra la méthode exposée paragraphe 11.4.1 et on renommera [Formulaire.aspx] la page [WebForm1.aspx] ainsi générée. L'aspect visuel de la page [Formulaire.aspx] en cours de construction sera le suivant :

L'aspect visuel de la page [Formulaire.aspx] a deux éléments :

  • en [1] la page maître avec son conteneur [ContentPlaceHolder1] (2)
  • en [2] les composants placés dans le conteneur [ContentPlaceHolder1]. Ceux-ci sont identiques à ceux de l'application précédente.

Le code source de cette page est le suivant :


<%@ 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>
  • ligne 1 : la directive Page avec son attribut MasterPageFile
  • ligne 4 : la classe de contrôle de la page maître peut exposer des champs et propriétés publics. Ceux-ci sont accessibles aux pages encapsulées avec la syntaxe Master.[champ] ou Master.[propriété]. La propriété Master de la page désigne la page maître sous la forme d'une instance de type [System.Web.UI.MasterPage]. Aussi dans notre exemple, faudrait-il écrire en réalité (MasterPage)(Master).[champ] ou (MasterPage)(Master).[propriété]. On peut éviter ce transtypage en insérant dans la page la directive MasterType de la ligne 4. L'attribut VirtualPath de cette directive indique le fichier de la page maître. Le compilateur peut alors connaître les champs, propriétés et méthodes publics exposés par la classe de la page maître, ici de type [MasterPage].
  • lignes 5-22 : le contenu qui sera inséré dans le conteneur [ContentPlaceHolder1] de la page maître.

On pourra construire cette page en mettant comme contenu (lignes 6-21), celui de la vue [VueSaisies] décrite au paragraphe 8.5.3 et celui de la vue [VueSimulation] décrite au paragraphe 8.5.4.

11.4.3. La page [Simulations.aspx]

Pour générer cette page, on suivra la méthode exposée paragraphe 11.4.1 et on renommera [Simulations.aspx] la page [WebForm1.aspx] ainsi générée. L'aspect visuel de la page [Simulations.aspx] en cours de construction est le suivant :

L'aspect visuel de la page [Simulations.aspx] a deux éléments :

  • en [1] la page maître avec son conteneur [ContentPlaceHolder1]
  • en [2] les composants placés dans le conteneur [ContentPlaceHolder1]. Ceux-ci sont identiques à ceux de l'application précédente.

Le code source de cette page est le suivant :


<%@ 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>

On pourra construire cette page en mettant comme contenu (lignes 5-21), celui de la vue [VueSimulations] décrite au paragraphe 8.5.5 et celui de la vue [VueSimulationsVides] décrite au paragraphe 8.5.6.

11.4.4. La page [Erreurs.aspx]

Pour générer cette page, on suivra la méthode exposée paragraphe 11.4.1 et on renommera [Erreurs.aspx] la page [WebForm1.aspx] ainsi générée. L'aspect visuel de la page [Erreurs.aspx] en cours de construction est le suivant :

L'aspect visuel de la page [Erreurs.aspx] a deux éléments :

  • en [1] la page maître avec son conteneur [ContentPlaceHolder1]
  • en [2] les composants placés dans le conteneur [ContentPlaceHolder1]. Ceux-ci sont identiques à ceux de l'application précédente.

Le code source de cette page est le suivant :


<%@ 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. Le code de contrôle des pages

11.5.1. Vue d'ensemble

Revenons à l'architecture de l'application :

  • [Global] est l'objet de type [HttpApplication] qui initialise (étape 0) l'application. Cette classe est identique à celle de la version précédente.
  • le code du contrôleur, qui était dans la version précédente, tout entier dans [Default.aspx.cs] est désormais réparti sur plusieurs pages :
    • [MasterPage.master] : la page maître des pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. Elle contient le menu.
    • [Formulaire.aspx] : la page qui présente le formulaire de simulation et gère les actions qui ont lieu sur ce formulaire
    • [Simulations.aspx] : la page qui présente la liste des simulations et gère les actions qui ont lieu sur cette même page
    • [Erreurs.aspx] : la page qui est affichée lors d'une erreur d'initialisation de l'application. Il n'y a pas d'actions possibles sur cette page.

Le traitement d'une demande d'un client se déroule selon les étapes suivantes :

  1. le client fait une demande à l'application. Il la fait normalement à l'une des deux pages [Formulaire.aspx, Simulations.aspx], mais rien ne l'empêche de demander la page [Erreurs.aspx]. Il faudra prévoir ce cas.
  2. la page demandée traite cette demande (étape 1). Pour ce faire, elle peut avoir besoin de l'aide de la couche [métier] (étape 2) qui elle-même peut avoir besoin de la couche [dao] si des données doivent être échangées avec la base de données. L'application reçoit une réponse de la couche [métier].
  3. selon celle-ci, elle choisit (étape 3) la vue (= la réponse) à envoyer au client et lui fournit (étape 4) les informations (le modèle) dont elle a besoin. Nous avons vu trois possibilités pour générer cette réponse :
    • la page (D) demandée est également la page (R) envoyée en réponse. Construire le modèle de la réponse (R) consiste alors à donner à certains des composants de la page (D), la valeur qu'ils doivent avoir dans la réponse.
    • la page (D) demandée n'est pas la page (R) envoyée en réponse. La page (D) peut alors :
      • transférer le flux d'exécution à la page (R) par l'instruction Server.Transfer(" R "). Le modèle peut alors être placé dans le contexte par Context.Items(" clé ")=valeur ou plus rarement dans la session par Session.Items(" clé ")=valeur
      • rediriger le client vers la page (R) par l'instruction Response.redirect(" R "). Le modèle peut alors être placé dans la session mais pas dans le contexte.
  4. la réponse est envoyée au client (étape 5)

Chacune des pages [MasterPage.master, Formulaire.aspx, Simulations.aspx, Erreurs.aspx] répondra à un ou plusieurs des événements ci-dessous :

  • Init : premier événement dans le cycle de vie de la page
  • Load : se produit au chargement de la page
  • Click : le clic sur l'un des liens du menu de la page maître

Nous traitons les pages les unes après les autres en commençant par la page maître.

11.5.2. Code de contrôle de la page [MasterPage.master]

11.5.2.1. Squelette de la classe

Le code de contrôle de la page maître a le squelette suivant :


using System.Web.UI.WebControls;

namespace pam_v7
{
  public partial class MasterPage : System.Web.UI.MasterPage
  {

    // le menu 
    public LinkButton OptionFaireSimulation
    {
      get { return LinkButtonFaireSimulation; }
    }
...

    // fixer le menu 
    public void SetMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
    {
....
    }

    // gestion de l'option [Terminer la session] 
    protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
    {
....
    }

    // init master page 
    protected void Page_Init(object sender, System.EventArgs e)
    {
....
      }
    }
  }
}
  • ligne 5 : la classe s'appelle [MasterPage] et dérive de la classe système [System.Web.UI.MasterPage].
  • lignes 9-14 : les 6 options du menu sont exposées comme propriétés publiques de la classe
  • lignes 16-19 : la méthode publique SetMenu va permettre aux pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] de fixer le menu de la page maître
  • lignes 22-25 : la procédure qui va gérer le clic sur le lien [LinkButtonTerminerSession]
  • lignes 28-31 : la procédure de gestion de l'événement Init de la page maître

11.5.2.2. Propriétés publiques de la classe


using System.Web.UI.WebControls;

namespace pam_v7
{
  public partial class MasterPage : System.Web.UI.MasterPage
  {

    // le 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; }
    }

...
  }
}

Pour comprendre ce code, il faut se rappeler les composants qui forment la page maître :

Type
Nom
Rôle
A
Panel (rose ci-dessus)
entete
entête de la page
B
Panel (jaune ci-dessus)
contenu
contenu de la page
1
LinkButton
LinkButtonFaireSimulation
demande le calcul de la simulation
2
LinkButton
LinkButtonEffacerSimulation
efface le formulaire de saisie
3
LinkButton
LinkButtonVoirSimulations
affiche la liste des simulations déjà faites
4
LinkButton
LinkButtonFormulaireSimulation
ramène au formulaire de saisie
5
LinkButton
LinkButtonEnregistrerSimulation
enregistre la simulation courante dans la liste des simulations
6
LinkButton
LinkButtonTerminerSession
abandonne la session courante

Les composants 1 à 6 ne sont pas accessibles en-dehors de la page qui les contient. Les propriétés des lignes 9 à 37 visent à les rendre accessibles aux classes externes, ici les classes des autres pages de l'application.

11.5.2.3. La méthode SetMenu

La méthode publique SetMenu permet aux pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] de fixer le menu de la page maître. Son code est basique :


        // 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. La gestion des événements de la page maître

La page maître va gérer deux événements :

  • l'événement Init qui est le premier événement du cycle de vie de la page
  • l'événement Click sur le lien [LinkButtonTerminerSession]

La page maître a cinq autres liens : [LinkButtonFaireSimulation, LinkButtonEnregistrerSimulation, LinkButtonEffacerSimulation, LinkButtonVoirSimulations, LinkButtonFormulaireSimulation]. Comme exemple, examinons ce qu'il faudrait faire lors d'un clic sur le lien [LinkButtonFaireSimulation] :

  1. vérifier les données saisies (heures, jours) dans la page [Formulaire.aspx]
  2. faire le calcul du salaire
  3. afficher les résultats dans la page [Formulaire.aspx]

Les opérations 1 et 3 impliquent d'avoir accès aux composants de la page [Formulaire.aspx]. Ce n'est pas le cas. En effet, la page maître n'a aucune connaissance des composants des pages susceptibles d'être insérées dans son conteneur [ContentPlaceHolder1]. Dans notre exemple, c'est à la page [Formulaire.aspx] de gérer le clic sur le lien [LinkButtonFaireSimulation] car c'est elle qui est affichée lorsqu'a lieu cet événement. Comment peut-elle être avertie de celui-ci ?

  • le lien [LinkButtonFaireSimulation] ne faisant pas partie de la page [Formulaire.aspx], on ne peut pas écrire dans [Formulaire.aspx] la procédure habituelle :

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

On peut contourner le problème avec le code suivant dans [Formulaire.aspx] :


using System.Collections.Generic;
...

namespace pam_v7
{
    public partial class Formulaire : System.Web.UI.Page
    {
// chargement de la page 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // gestionnaire d'évts
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
...
    }

    // calcul de la paie 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
....
    }

    // effacer la 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)
    {
...
    }
 }
}
  • lignes 12-15 : lorsque l'événement Load de la page [Formulaire.aspx] se produit, la classe [MasterPage] de la page maître a été instanciée. Ses propriétés publiques Optionxx sont accessibles et sont de type LinkButton, un composant qui supporte l'événement Click. Nous associons à ces événements Click les méthodes :
    • OptFaireSimulation_Click pour l'événement Click sur le lien LinkButtonFaireSimulation
    • OptEffacerSimulation_Click pour l'événement Click sur le lien LinkButtonEffacerSimulation
    • OptVoirSimulations_Click pour l'événement Click sur le lien LinkButtonVoirSimulations
    • OptEnregistrerSimulation_Click pour l'événement Click sur le lien LinkButtonEnregistrerSimulation

La gestion des événements Click sur les six liens du menu sera répartie de la façon suivante :

  • la page [Formulaire.aspx] gèrera les liens [LinkButtonFaireSimulation, LinkButtonEnregistrerSimulation, LinkButtonEffacerSimulation, LinkButtonVoirSimulations]
  • la page [Simulations.aspx] gèrera le lien [LinkButtonFormulaireSimulation]
  • la page maître [MasterPage.master] gèrera le lien [LinkButtonTerminerSession]. Pour cet événement, elle n'a en effet pas besoin de connaître la page qu'elle encapsule.

11.5.2.5. L'événement Init de la page maître

Les trois pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] de l'application ont [MasterPage.master] pour page maître. Appelons M la page maître, E la page encapsulée. Lorsque la page E est demandée par le client, les événements suivants se produisent dans l'ordre :

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

Nous allons utiliser l'événement Init de la page M pour exécuter du code qu'il serait intéressant d'exécuter le plus tôt possible, ceci quelque soit la page cible E. Pour découvrir ce code, revoyons l'image d'ensemble de l'application :

Ci-dessus, [Global] est l'objet de type [HttpApplication] qui initialise l'application. Cette classe est la même que dans la version [pam-v4-3tier-nhibernate-multivues-monopage] :


using System;
...

namespace pam_v7
{
  public class Global : System.Web.HttpApplication
  {
    // --- données statiques de l'application ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;
    public static string Msg;
    public static bool Erreur = false;

    // démarrage de l'application
    public void Application_Start(object sender, EventArgs e)
    {
...
    }

    public void Session_Start(object sender, EventArgs e)
    {
...
    }
  }
}

Si la classe [Global] ne réussit pas à initialiser correctement l'application, elle positionne deux variables publiques statiques :

  • le booléen Erreur de la ligne 12 est mis à vrai
  • la variable Msg de la ligne 11 contient un message donnant des détails sur l'erreur rencontrée

Lorsque l'utilisateur demande l'une des pages [Formulaire.aspx, Simulations.aspx] alors que l'application ne s'est pas initialisée correctement, cette demande doit être transférée ou redirigée vers la page [Erreurs.aspx], qui affichera le message d'erreur de la classe [Global]. On peut gérer ce cas de diverses façons :

  • faire le test d'erreur d'initialisation dans le gestionnaire des événements Init ou Load de chacune des pages [Formulaire.aspx, Simulations.aspx]
  • faire le test d'erreur d'initialisation dans le gestionnaire des événements Init ou Load de la page maître de ces deux pages. Cette méthode a l'avantage de placer le test d'erreur d'initialisation à un unique endroit.

Nous choisissons de faire le test d'erreur d'initialisation dans le gestionnaire de l'événement Init de la page maître :


        protected void Page_Init(object sender, System.EventArgs e)
        {
            // gestionnaire d'évts 
            LinkButtonTerminerSession.Click += LinkButtonTerminerSession_Click;
            // des erreurs d'initialisation ? 
            if (Global.Erreur)
            {
                // la page encapsulée est-elle la page d'erreurs ? 
                bool isPageErreurs =...;
                // si c'est la page d'erreurs qui s'affiche, on laisse faire sinon on redirige le client vers la page d'erreurs 
                if (!isPageErreurs)
                    Response.Redirect("Erreurs.aspx");
                return;
            }
}

Le code ci-dessus va s'exécuter dès que l'une des pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx] va être demandée. Dans le cas où la page demandée est [Formulaire.aspx, Simulations.aspx], on se contente (ligne 12) de rediriger le client vers la page [Erreurs.aspx], celle-ci se chargeant d'afficher le message d'erreur de la classe [Global]. Dans le cas où la page demandée est [Erreurs.aspx], cette redirection ne doit pas avoir lieu : il faut laisser la page [Erreurs.aspx] s'afficher. Il nous faut donc savoir dans la méthode [Page_Init] de la page maître, quelle est la page que celle-ci encapsule.

Revenons sur l'arbre des composants de la page maître :


...
<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>
  • lignes 1-13 : le conteneur d'id "form1"
  • lignes 4-6 : le conteneur d'id "entete", inclus dans le conteneur d'id "form1"
  • lignes 8-11 : le conteneur d'id "contenu", inclus dans le conteneur d'id "form1"
  • lignes 9-10 : le conteneur d'id "ContentPlaceHolder1", inclus dans le conteneur d'id "contenu"

Une page E encapsulée dans la page maître M, l'est dans le conteneur d'id "ContentPlaceHolder1". Pour référencer un composant d'id C de cette page E, on écrira :


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

L'arbre des composants de la page [Erreurs.aspx] est lui, le suivant :


<%@ 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>

Lorsque la page [Erreurs.aspx] est fusionnée avec la page maître M, le contenu de la balise <asp:Content> ci-dessus (lignes 5-16), est intégré dans la balise <asp:ContentPlaceHolder> d'id "ContentPlaceholder1" de la page M, l'arbre des composants de celle-ci devenant alors :

...
<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>
  • ligne 12 : le composant [rptErreurs] peut être utilisé pour savoir si la page maître M contient ou non la page [Erreurs.aspx]. En effet, ce composant n'existe que dans cette page.

Ces explications suffisent pour comprendre le code de la procédure [Page_Init] de la page maître :


protected void Page_Init(object sender, System.EventArgs e)
        {
            // gestionnaire d'évts 
            LinkButtonTerminerSession.Click += LinkButtonTerminerSession_Click;
            // des erreurs d'initialisation ? 
            if (Global.Erreur)
            {
                // la page encapsulée est-elle la page d'erreurs ? 
                bool isPageErreurs = this.FindControl("form1").FindControl("contenu").FindControl("ContentPlaceHolder1").FindControl("rptErreurs") != null;
                // si c'est la page d'erreurs qui s'affiche, on laisse faire sinon on redirige le client vers la page d'erreurs 
                if (!isPageErreurs)
                    Response.Redirect("Erreurs.aspx");
                return;
            }
        }
  • ligne 4 : on associe un gestionnaire d'événement à l'événement Click sur le lien LinkButtonTerminerSession. Ce gestionnaire est dans la classe MasterPage.
  • ligne 6 : on vérifie si la classe [Global] a positionné son booléen Erreur
  • ligne 9 : si oui, le booléen IsPageErreurs indique si la page encapsulée dans la page maître est la page [Erreurs.aspx]
  • ligne 12 : si la page encapsulée dans la page maître n'est pas la page [Erreurs.aspx], alors on redirige le client vers cette page, sinon on ne fait rien.

11.5.2.6. L'événement Click sur le lien [LinkButtonTerminerSession]

Lorsque l'utilisateur clique sur le lien [Terminer la session] dans la vue (1) ci-dessus, il faut vider la session de son contenu et présenter un formulaire vide (2).

Le code du gestionnaire de cet événement pourrait être le suivant :


        protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
        {
            // on abandonne la session 
            Session.Abandon();
            // on affiche la vue [formulaire] 
            Response.Redirect("Formulaire.aspx");
}
  • ligne 4 : la session courante est abandonnée
  • ligne 6 : le client est redirigé vers la page [Formulaire.aspx]

On voit que ce code ne fait intervenir aucun des composants des pages [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]. L'événement peut donc être géré par la page maître elle-même.

11.5.3. Code de contrôle de la page [Erreurs.aspx]

Le code de contrôle de la page [Erreurs.aspx] pourrait être le suivant :


using System.Collections.Generic;

namespace pam_v7
{
  public partial class Erreurs : System.Web.UI.Page
  {
    protected void Page_Load(object sender, System.EventArgs e)
    {
      // des erreurs d'initialisation ? 
      if (Global.Erreur)
      {
        // on prépare le modèle de la page [erreurs] 
        List<string> erreursInitialisation = new List<string>();
        erreursInitialisation.Add(Global.Msg);
        // on associe la liste d'erreurs à son composant 
        rptErreurs.DataSource = erreursInitialisation;
        rptErreurs.DataBind();
      }
      // on fixe le menu 
      Master.SetMenu(false, false, false, false, false, false);
    }
  }
}

Rappelons que la page [Erreurs.aspx] a pour unique rôle d'afficher une erreur d'initialisation de l'application lorsque celle-ci se produit :

  • ligne 10 : on teste si l'initialisation s'est terminée par une erreur
  • lignes 13-14 : si oui, le message d'erreur (Global.Msg) est placé dans une liste [ErreursInitialisation]
  • lignes 16-17 : on demande au composant [rptErreurs] d'afficher cette liste
  • ligne 20 : dans tous les cas (erreur ou pas), les options du menu de la page maître ne sont pas affichées, de sorte que l'utilisateur ne peut débuter aucune nouvelle action à partir de cette page.

Que se passe-t-il si l'utilisateur demande directement la page [Erreurs.aspx] (ce qu'il n'est pas supposé faire dans une utilisation normale de l'application) ? En suivant le code de [MasterPage.master.cs] et de [Erreurs.aspx.cs], on s'apercevra que :

  • s'il y a eu erreur d'initialisation, celle-ci est affichée
  • s'il n'y a pas eu erreur d'initialisation, l'utilisateur reçoit une page ne contenant que l'entête de [MasterPage.master] avec aucune option de menu affichée.

11.5.4. Code de contrôle de la page [Formulaire.aspx]

11.5.4.1. Squelette de la classe

Le squelette du code de contrôle de la page [Formulaire.aspx] pourrait être le suivant :


using Pam.Metier.Entites;
...

partial class PageFormulaire : System.Web.UI.Page
{

    // chargement de la page 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // gestionnaire d'évts
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
....
    }

    // calcul de la paie 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
....
    }

    // effacer la 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)
    {
...
    }

}

Le code de contrôle de la page [Formulaire.aspx] gère cinq événements :

  1. l'événement Load de la page
  2. l'événement Click sur le lien [LinkButtonFaireSimulation] de la page maître
  3. l'événement Click sur le lien [LinkButtonEffacerSimulation] de la page maître
  4. l'événement Click sur le lien [LinkButtonEnregistrerSimulation] de la page maître
  5. l'événement Click sur le lien [LinkButtonVoirSimulations] de la page maître

11.5.4.2. Evénement Load de la page

Le squelette du gestionnaire de l'événement Load de la page pourrait être le suivant :


    protected void Page_Load(object sender, System.EventArgs e)
    {
        // gestionnaire d'évts
        Master.OptionFaireSimulation.Click += OptFaireSimulation_Click;
        Master.OptionEffacerSimulation.Click += OptEffacerSimulation_Click;
        Master.OptionVoirSimulations.Click += OptVoirSimulations_Click;
        Master.OptionEnregistrerSimulation.Click += OptEnregistrerSimulation_Click;
        // affichage vue [saisies] 
        ...
        // positionnement menu page maître 
        ...
        // traitement requête GET 
        if (!IsPostBack)
        {
            // chargement des noms des employés dans le combo 
...
            // init vue [saisies] avec saisies mémorisées dans la session si elles existent 
....
        }
}

Un exemple pour éclaircir le commentaire de la ligne 17 pourrait être celui-ci :

  • en [1], on demande à voir la liste des simulations. Des saisies ont été faites en [A, B, C].
  • en [2], on voit la liste
  • en [3], on demande à retourner au formulaire
  • en [4], on retrouve le formulaire tel qu'on l'a laissé. Comme il y a eu deux requêtes, (1,2) et (3,4), cela signifie que :
    • lors du passage de [1] à [2], les saisies de [1] ont été mémorisées
    • lors du passage de [3] à [4], elles ont été restituées. C'est la procédure [Page_Load] de [Formulaire.aspx] qui opére cette restitution.

Question : compléter la procédure Page_Load en vous aidant des commentaires et du code de la version [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.4.3. Gestion des événements Click sur les liens du menu

Le squelette des gestionnaires des événements Click sur les liens de la page maître est le suivant :


// calcul de la paie 
    private void OptFaireSimulation_Click(object sender, System.EventArgs e)
    {
        // effet Ajax
        Thread.Sleep(3000);
        // page valide ? 
        Page.Validate();
        if (!Page.IsValid)
        {
            // affichage vue [saisie] 
...
        }
        // la page est valide - on récupère les saisies 
...
        // on calcule le salaire de l'employé 
        FeuilleSalaire feuillesalaire;
        try
        {
            feuillesalaire = ...;
        }
        catch (PamException ex)
        {
            // on a rencontré un problème 
...
            return;
        }
        // on met le résultat dans la session 
        Session["simulation"] = ...;
        // on met les saisies dans la session 
...
        // affichage 
...
        // affichage vues 
...
        // affichage menu MasterPage 
...
    }

    // effacer la simulation 
    private void OptEffacerSimulation_Click(object sender, System.EventArgs e)
    {
        // affichage panel [saisie] 
...
        // sélection 1er employé 
...
    }

    protected void OptVoirSimulations_Click(object sender, System.EventArgs e)
    {
        // on met les saisies dans la session 
...
        // on affiche la vue [simulations] 
        Response.Redirect("simulations.aspx");
    }

    protected void OptEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
        // on enregistre la simulation courante dans la session de l'utilisateur 
...
        // on affiche la vue [simulations] 
        Response.Redirect("simulations.aspx");
}

Question : compléter le code des procédures ci-dessus en vous aidant des commentaires et du code de la version [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.5. Code de contrôle de la page [Simulations.aspx]

Le squelette du code de contrôle de la page [Simulations.aspx] pourrait être le suivant :


using System.Collections.Generic;
using Pam.Web;
using System.Web.UI.WebControls;

partial class PageSimulations : System.Web.UI.Page
{

    // les simulations 
    private List<Simulation> simulations;

    // chargement de la page
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // gestionnaire d'évts 
        Master.OptionFormulaireSimulation.Click += OptFormulaireSimulation_Click;
        GridViewSimulations.RowDeleting += GridViewSimulations_RowDeleting;
        // on récupère les simulations dans la session
        simulations = ...;
        // y-a-t-il des simulations ? 
        if (simulations.Count != 0)
        {
            // première vue visible 
            ...
            // on remplit le gridview 
            ...
        }
        else
        {
            // seconde vue 
            ...
        }
        // on fixe le menu 
        ...
    }

    protected void GridViewSimulations_RowDeleting(object sender, System.Web.UI.WebControls.GridViewDeleteEventArgs e)
    {
        // on récupère les simulations dans la session
        List<Simulation> simulations = ...;
        // on supprime la simulation désignée (e.RowIndex représente le n° de la ligne supprimée dans le gridview)
        ..
        // reste-t-il des simulations ? 
        if (simulations.Count != 0)
        {
            // on remplit le gridview 
            ...
        }
        else
        {
            // vue [SimulationsVides] 
            ...
        }
    }

    protected void OptFormulaireSimulation_Click(object sender, System.EventArgs e)
    {
        // on affiche la vue [formulaire] 
        Response.Redirect("formulaire.aspx");
    }

}

Question : compléter le code des procédures ci-dessus en vous aidant des commentaires et du code de la version [pam-v4-3tier-nhibernate-multivues-monopage]


11.5.6. Code de contrôle de la page [Default.aspx]

On peut prévoir une page [Default.aspx] dans l'application, afin de permettre à l'utilisateur de demander l'URL de l'application sans préciser de page, comme ci-dessous :

La demande [1] a reçu en réponse la page [Formulaire.aspx] (2). On sait que la demande (1) est traitée par défaut par la page [Default.aspx] de l'application. Pour obtenir (2), il suffit que [Default.aspx] redirige le client vers la page [Formulaire.aspx]. Cela peut être obtenu avec le code suivant :


partial class _Default : System.Web.UI.Page
{

    protected void Page_Init(object sender, System.EventArgs e)
    {
        // on redirige vers le formulaire de saisie 
        Response.Redirect("Formulaire.aspx");
    }
}

La page de présentation [Default.aspx] ne contient elle que la directive qui la relie à [Default.aspx.cs] :


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