Skip to content

11. [SimuPaie] 应用程序 – 第 7 版 – ASP.NET / 多视图 / 多页面


推荐阅读:参考文献[1],《ASP.NET 1.1 Web开发》,章节:示例


我们现在正在研究一个版本,其功能与之前讨论的三层 ASP.NET 应用程序 [pam-v4-3tier-nhibernate-multivues-monopage] 完全相同,但我们将对其架构进行如下修改:在之前的版本中,视图由单个 ASPX 页面实现,而在这里,它们将由三个 ASPX 页面实现。

先前应用程序的架构如下:

这里采用的是 MVC(模型-视图-控制器)架构:

  • [Default.aspx.cs] 包含控制器代码。[Default.aspx] 页面是客户端的唯一交互入口,负责处理来自客户端的所有请求。
  • [Saisies、Simulation、Simulations、...] 均为视图。这些视图通过在 [Default.aspx] 页面上使用 [View] 组件来实现。

新版本的架构将如下所示:

  • 仅 [web] 层发生变更
  • 视图(即呈现给用户的内容)保持不变。
  • 控制器代码——在上一版本中完全位于 [Default.aspx.cs] 中——现在分布在多个页面中:
    • [MasterPage.master]:一个封装了不同视图中通用元素的页面:顶部横幅及其菜单选项
    • [Formulaire.aspx]:用于显示模拟表单并管理该表单上操作的页面
    • [Simulations.aspx]:显示模拟列表并处理该页面上操作的页面
    • [Errors.aspx]:应用程序初始化发生错误时显示的页面。该页面不支持任何操作。

这可以视为多控制器 MVC 架构,而上一版本的架构则是单控制器 MVC 架构。

处理客户端请求遵循以下步骤:

  1. 客户端向应用程序发起请求。请求会发送到两个页面之一 [Formulaire.aspx, Simulations.aspx]。
  2. 被请求的页面处理该请求。为此,它可能需要 [业务] 层的协助,而如果需要与数据库交换数据,[业务] 层本身可能又需要 [DAO] 层的协助。应用程序从 [业务] 层接收响应。
  3. 基于该响应,应用程序选择 (3) 要发送给客户端的视图(即响应),并向其 (4) 提供所需的信息(即模型)。
  4. 响应被发送给客户端 (5)

11.1. 应用程序的视图

向用户展示的不同视图如下:

  • - [VueSaisies] 视图,用于显示模拟表单

Image

  • - [VueSimulation] 视图,用于显示详细的模拟结果:

Image

  • - [SimulationView] 视图,用于列出客户端执行的模拟

Image

  • - [EmptySimulationsView] 视图,表示客户端没有模拟或已无更多模拟:

Image

  • [ErrorView] 视图,表示应用程序初始化错误:

Image

11.2. 在多控制器环境中生成视图

在上一版本中,所有视图均由单个 [Default.aspx] 页面生成。该页面包含两个 [MultiView] 组件,而视图由属于这两个 [MultiView] 组件的一个或两个 [View] 组件组合而成。

虽然在视图较少时这种架构很有效,但一旦构成各种视图的组件数量变大,该架构就会达到极限:事实上,每次向单个 [Default.aspx] 页面发送请求时,其所有组件都会被实例化,尽管其中只有一部分会被用于生成给用户的响应。因此,每次新请求都会执行不必要的工作,当页面上的组件总数较大时,这便会成为瓶颈。

一种解决方案是将视图分布在不同的页面上。这就是我们在此要做的。让我们来考察两种不同的视图生成情况:

  1. 请求发送到页面 P1,由其生成响应
  2. 向页面 P1 发起请求,该页面请求页面 P2 生成响应

11.2.1. 情况 1:控制器/视图页面

在情况 1 中,我们回归到上一版本的单控制器架构,其中 [Default.aspx] 页面即为页面 P1:

  1. 客户端向页面 P1 发起请求 (1)
  2. 页面 P1 处理此请求。为此,它可能需要 [业务] 层的协助(2),而该层若需与数据库交换数据,则可能需要 [DAO] 层的支持。应用程序从 [业务] 层接收响应。
  3. 基于该响应,它选择 (3) 要发送给客户端的视图(即响应),并向客户端 (4) 提供其所需的信息(模型)。这包括选择要在页面 P1 上显示的 [Panel] 或 [View] 组件,并初始化这些组件所包含的内容。
  4. 响应被发送至客户端(5)

以下是取自所研究应用程序的两个示例:

[Formulaire.aspx 页面]

  • 在 [1] 中:请求 [Formulaire.aspx] 页面后,用户请求了一次模拟
  • 在 [2] 中:[Formulaire.aspx] 页面处理了此请求,并通过显示一个在 [1] 中未显示的 [View] 组件,自行生成了响应

[页面 Simulations.aspx]

  • 在 [1] 中:请求 [Simulations.aspx] 页面后,用户希望删除一个模拟
  • 在 [2] 中:[Simulations.aspx] 页面处理了此请求,并通过重新显示新的模拟列表来生成响应。

11.2.2. 情况 2:单控制器页面,控制器/视图页面

情况 2 涵盖多种架构。我们将选择以下方案:

  1. 客户端向页面 P1 发起请求 (1)
  2. 页面 P1 处理此请求。为此,它可能需要 [业务] 层的协助(2),而该层若需与数据库交换数据,则可能需要 [DAO] 层的支持。应用程序从 [业务] 层接收响应。
  3. 基于此,它选择(3)要发送给客户端的视图(即响应),并通过提供(4)所需的信息(模板)来完成这一操作。在此情况下,待生成的视图必须由 P1 以外的页面创建——具体而言,是页面 P2。为了执行操作(3)和(4),页面 P1 有两种选择:
    • 使用 [Server.Transfer("P2.aspx")] 操作将执行转移到页面 P2。在这种情况下,它可以将面向页面 P2 的模型放置在请求上下文中 [Context.Items["key"]=value] 或用户会话中 [Session.["key"]=value]。 随后页面 P2 将被实例化,当处理其 Load 事件时,例如,它可以使用 [value=(Type)Context.Items["key"] 或 [value=(Type)Session["key"] 操作来检索页面 P1 传递的信息,具体取决于情况,其中 Type 是与该键关联的值的类型。 如果不需要为未来的客户端请求保留模型值,则通过 Context 传输值最为合适。
    • 要求客户端使用操作 [Response.Redirect("P2.aspx")] 重定向到页面 P2。 在这种情况下,由于请求上下文会在每次请求结束时被清除,因此页面 P1 会将本应提供给页面 P2 的模型放入会话中。然而,此处的重定向将导致客户端对 P1 的首次请求结束,并触发来自同一客户端的第二次请求,这次是针对 P2 的。存在两个连续的请求。我们知道会话是保留请求之间“记忆”的一种方式。除了会话之外,还有其他解决方案。
  4. 无论 P2 如何接管,我们随后将回到情况 1:P2 已收到一个请求并将其处理(5),并自行生成响应(6、7)。我们还可以设想,在处理完请求后,页面 P2 会将请求移交给页面 P3,以此类推。

以下是一个取自正在研究的应用程序的示例:

  • 在 [1] 中:请求 [Formulaire.aspx] 页面的用户要求查看 [2] 中的模拟列表
  • 在[2]中:[Formulaire.aspx]页面处理此请求,并将客户端重定向至[Simulations.aspx]页面。正是后者向用户提供了响应。与要求客户端重定向不同,[Formulaire.aspx]页面本可以将客户端的请求转发至[Simulations.aspx]页面。 在[2]中的这种情况下,我们看到的URL将与[1]中相同。事实上,浏览器始终显示最后请求的URL:
    • [1] 中请求的操作针对的是 [Formulaire.aspx] 页面。浏览器向该页面发送一个 POST 请求。
    • 如果 [Formulaire.aspx] 页面处理该请求后,通过 [Server.Transfer("Simulations.aspx")] 将其转发至 [Simulations.aspx] 页面,则我们仍处于同一请求中。此时,浏览器将在 [2] 中显示 [Formulaire.aspx] 的 URL(即 POST 请求的发送目标)。
    • 如果 [Formulaire.aspx] 页面处理请求后,通过 [Response.Redirect("Simulations.aspx")] 将其重定向至 [Simulations.aspx] 页面,则浏览器会发起第二个请求,即向 [Simulations.aspx] 发送的 GET 请求。 此时,浏览器将在 [2] 处显示该 GET 请求所发送的 [Simulations.aspx] 的 URL。这就是上文截图 [2] 所展示的内容。

11.3. [web] 层的 Visual Web Developer 项目

[web] 层的 Visual Web Developer 项目如下:

  • 在 [1] 中,我们发现:
    • 应用程序的配置文件 [Web.config]——其内容与 [pam-v4-3tier-nhibernate-multivues-monopage] 应用程序的配置文件完全一致。
    • [Default.aspx] 页面——仅将客户端重定向至 [Formulaire.aspx] 页面
    • [Formulaire.aspx] 页面,向用户显示模拟表单并处理与该表单相关的操作
    • [Simulations.aspx] 页面,用于显示用户的模拟列表并处理与该页面相关的操作
    • [Errors.aspx] 页面,向用户显示一个页面,指示 Web 应用程序启动时遇到的错误。
  • 在 [2] 中,我们可以看到项目引用。

让我们回到新项目的架构:

与 [pam-v4-3tier-nhibernate-multivues-monopage] 项目相比,仅视图部分发生了变化。这就是为什么新项目复用了该项目中的部分文件:

  • 配置文件 [Web.config]
  • 引用的 DLL 文件 [pam-dao-nhibernate, pam-metier-dao-nhibernate, Spring.Core, NHibernate]
  • 全局应用程序类 [Global.asax]
  • 文件夹 [images, resources, pam]

为了与当前正在开发的项目保持一致,我们将确保视图和全局应用程序类的命名空间为 [pam-v7]:

  

11.4. 页面呈现代码

11.4.1. 母版页 [MasterPage.master]

第 11.1 节中介绍的应用程序视图包含一些共同元素,这些元素可以提取到主页面中,在 Visual Studio 中称为“主页面”。以下文中的 [VueSaisies] 和 [VueSimulationsVides] 视图为例,它们分别由页面 [Formulaire.aspx] 和 [Simulations.aspx] 生成:

这两个视图共享顶部横幅(标题和菜单选项)。所有将呈现给用户的视图都是如此:它们都将拥有相同的顶部横幅。为了使不同的页面能够共享相同的呈现片段,有多种解决方案,包括以下方法:

  • 将此通用片段放置在用户控件中。这是 ASP.NET 1.1 中的主要技术
  • 将该通用片段放置在母版页中。该技术始于 ASP.NET 2.0。这也是我们在此采用的方法。

要在 Web 应用程序中创建母版页,请按照以下步骤操作:

  • 右键单击项目 / 添加新项 / 母版页:

添加母版页后,Web 应用程序中默认会生成三个文件:

  • [MasterPage.master]:母版页的布局代码
  • [MasterPage.master.cs]:主页面的控件代码
  • [MasterPage.Master.Designer.cs]:主页面的组件声明

Visual Studio 在 [MasterPage.master] 中生成的代码如下:


<%@ 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>
  • 第 1 行:<%@ Master ... %> 标签用于将该页面定义为主页面。页面的控件代码将位于由 CodeBehind 属性指定的文件中,且该页面将继承自由 Inherits 属性指定的类。
  • 第 12–18 行:母版页表单
  • 第 14–16 行:一个空容器,在我们的应用程序中,它将容纳其中一个页面 [Form.aspx、Simulations.aspx、Errors.aspx]。 客户端收到的响应始终是同一页面——即母版页——其中 [ContentPlaceHolder1] 容器将接收由 [Form.aspx、Simulations.aspx、Errors.aspx] 中的某个页面提供的 HTML 流。因此,要更改发送给客户端的页面外观,只需更改母版页的外观即可。
  • 第 8–9 行:一个空容器,允许“子”页面自定义页眉 <head>...</head>。

该源代码的可视化表示(“设计”选项卡)如下图 (1) 所示。此外,您还可以通过 [标准] 工具栏中的 [ContentPlaceHolder] 组件 (2) 添加任意数量的容器。

Visual Studio 在 [MasterPage.master.cs] 中生成的控件代码如下:


using System;
 
public partial class MasterPage : System.Web.UI.MasterPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
 
    }
}
  • 第 3 行:[MasterPage.master] 页面中 <%@ Master ... %> 指令的 [Inherits] 属性所引用的类继承自 [System.Web.UI.MasterPage] 类

在上文中,我们可以看到 Page_Load 方法的存在,它负责处理母版页的 Load 事件。母版页内部将包含另一个页面。这两个页面的 Load 事件将按什么顺序发生?这是一条通用规则:组件的 Load 事件发生在其容器的 Load 事件之前。因此,在此处,嵌入母版页中的页面的 Load 事件将先于母版页本身的 Load 事件发生。

要在 中生成一个将前面的[MasterPage.master]页面用作其母版页的页面,可以按以下步骤操作:

  • 在 [1] 中:右键单击母版页,然后选择 [添加内容页]
  • 在 [2]:系统将生成一个默认页面,本例中为 [WebForm1.aspx]。

[WebForm1.aspx] 的呈现代码如下:


<%@ 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>
  • 第 1 行:Page 指令及其属性
    • MasterPageFile:指定该指令所描述页面的母版页文件。~ 符号表示项目文件夹。
    • 其余参数均为 ASP 网页的标准参数
  • 第 2–3 行:<asp:Content> 标签通过 ContentPlaceHolderID 属性依次与母版页中的 <asp:ContentPlaceHolder> 指令建立关联。上述第 2–3 行之间的组件将在运行时被放置到母版页上的 ContentPlaceHolder1 容器中。

通过重命名以此方式生成的页面 [WebForm1.aspx],我们可以使用 [MasterPage.master] 作为母版页来构建各种页面。

对于我们的 [SimuPaie] 应用程序,母版页的外观将如下所示:

编号
类型
名称
角色
A
面板(上方的粉色部分)
页眉
页面页眉
B
面板(上图为黄色)
内容
页面内容
1
链接按钮
LinkButtonRunSimulation
请求进行模拟计算
2
链接按钮
LinkButtonClearSimulation
清除输入表单
3
链接按钮
LinkButtonViewSimulations
显示已执行的模拟列表
4
链接按钮
LinkButtonSimulationForm
返回输入表单
5
LinkButton
LinkButtonSaveSimulation
将当前模拟保存到模拟列表中
6
链接按钮
LinkButtonEndSession
结束当前会话

相应的源代码如下:


<%@ 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>
  • 第 1 行:请注意母版页类的名称:MasterPage
  • 第 8 行:为页面定义背景图像。
  • 第 9–64 行:表单
  • 第 10 行:实现 Ajax 效果所需的 ScriptManager 组件
  • 第 11–63 行:AJAX 容器
  • 第 12–62 行:支持 Ajax 的内容
  • 第 13–55 行:Panel 组件 [header]
  • 第 57–60 行:Panel 组件 [content]
  • 第 58–59 行:[ContentPlaceHolder1] 组件,将包含封装的页面 [Formulaire.aspx, Simulations.aspx, Erreurs.aspx]

要构建此页面,我们可以将 [pam-v4-3tier-nhibernate-multivues-monopage] 版本中 [Default.aspx] 页面 [HeaderView] 视图的 ASPX 代码插入到 [header] 面板中,具体内容详见第 8.5.2 节。

11.4.2. [Formulaire.aspx] 页面

要生成此页面,请按照第 11.4.1 节所述的方法操作,并将生成的 [WebForm1.aspx] 页面重命名为 [Form.aspx]。正在构建的 [Form.aspx] 页面的视觉外观如下:

[Formulaire.aspx] 页面的视觉外观由两个元素组成:

  • [1] 主页面及其 [ContentPlaceHolder1] 容器 (2)
  • [2] 放置在 [ContentPlaceHolder1] 容器中的组件。这些组件与上一应用程序中的完全相同。

该页面的源代码如下:


<%@ 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>
  • 第 1 行:带有 MasterPageFile 属性的 Page 指令
  • 第 4 行:母版页控件类可以公开公共字段和属性。封装的页面可以通过 Master.[field] Master.[property] 语法访问这些字段和属性。页面的 Master 属性将母版页视为 [System.Web.UI.MasterPage] 类型的实例。因此,在我们的示例中,实际上应写为 (MasterPage)(Master).[field] (MasterPage)(Master).[property]。若在页面中插入第 4 行中的 MasterType 指令,即可避免这种类型转换。该指令的 VirtualPath 属性指定了母版页文件。这样,编译器就能识别母版页类(本例中为 [MasterPage] 类型)所公开的公共字段、属性和方法。
  • 第 5–22 行:将插入到母版页 [ContentPlaceHolder1] 容器中的内容。

可以通过将该页面的内容(第 6–21 行)设置为第 8.5.3 节中描述的 [VueSaisies] 视图以及第 8.5.4 节中描述的 [VueSimulation] 视图的内容来构建此页面。

11.4.3. [Simulations.aspx] 页面

要生成此页面,请按照第 11.4.1 节所述的方法操作,并将生成的 [WebForm1.aspx] 页面重命名为 [Simulations.aspx]。当前正在构建的 [Simulations.aspx] 页面的外观如下:

[Simulations.aspx] 页面的视觉外观由两个元素组成:

  • [1] 主页面及其 [ContentPlaceHolder1] 容器
  • 在 [2] 处,将组件放置在 [ContentPlaceHolder1] 容器中。这些组件与上一应用程序中的完全相同。

该页面的源代码如下:


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

我们可以将该页面的内容(第 5–21 行)设置为第 8.5.5 节中描述的 [VueSimulations] 视图以及第 8.5.6 节中描述的 [VueSimulationsVides] 视图的内容,从而构建此页面。

11.4.4. [Errors.aspx] 页面

要生成此页面,请按照第 11.4.1 节所述的方法操作,并将生成的 [WebForm1.aspx] 页面重命名为 [Errors.aspx]。当前正在构建的 [Errors.aspx] 页面的外观如下:

[Errors.aspx] 页面的视觉外观由两个元素组成:

  • [1] 主页面及其 [ContentPlaceHolder1] 容器
  • [2] 放置在 [ContentPlaceHolder1] 容器中的组件。这些组件与上一应用程序中的完全相同。

该页面的源代码如下:


<%@ 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. 页面控件代码

11.5.1. 概述

让我们回到应用程序架构:

  • [Global] 是负责初始化(步骤 0)应用程序的 [HttpApplication] 对象。该类与上一版本中的完全相同。
  • 控制器代码在上一版本中完全位于 [Default.aspx.cs] 中,现在则分布在多个页面中:
    • [MasterPage.master]:[Form.aspx、Simulations.aspx、Errors.aspx] 的母版页。其中包含菜单。
    • [Formulaire.aspx]:用于显示模拟表单并处理该表单上操作的页面
    • [Simulations.aspx]:显示模拟列表并处理该页面上操作的页面
    • [Errors.aspx]:应用程序初始化发生错误时显示的页面。该页面不支持任何操作。

处理客户端请求遵循以下步骤:

  1. 用户向应用程序发起请求。通常,用户会在 [Form.aspx] 或 [Simulations.aspx] 其中一个页面上进行操作,但用户也可以请求 [Errors.aspx] 页面。必须考虑到这种情况。
  2. 被请求的页面处理该请求(步骤 1)。为此,它可能需要 [业务] 层的协助(步骤 2),而该层若需与数据库交换数据,则可能需要 [DAO] 层的协助。应用程序从 [业务] 层接收响应。
  3. 基于此,它选择(步骤 3)要发送给客户端的视图(即响应),并为其(步骤 4)提供所需的信息(模型)。我们已经看到了生成此响应的三种可能性:
    • 请求的页面(D)即为响应中发送的页面(R)。构建响应模型(R)的过程,即为将页面(D)的某些组件赋予其在响应中必须具备的值。
    • 请求的页面 (D) 并非作为响应发送的页面 (R)。此时页面 (D) 可以:
      • 使用 Server.Transfer(" R ") 语句将执行流程转移至页面 (R)。随后可通过 Context.Items("key")=value 将模型放入上下文中,或较少见地通过 Session.Items("key")=value 放入会话中
      • 使用 Response.redirect(" R ") 语句将客户端重定向至页面 (R)。此时模型可存入会话,但无法存入上下文。
  4. 响应被发送给客户端(步骤 5)

每个页面 [MasterPage.master, Form.aspx, Simulations.aspx, Errors.aspx] 都会响应以下一个或多个事件:

  • Init:页面生命周期中的首个事件
  • Load:页面加载时触发
  • Click:用户点击母版页菜单中的某个链接时触发

我们依次处理页面,从母版页开始。

11.5.2. [MasterPage.master] 页面的控件代码

11.5.2.1. 类骨架

主页面的控件代码具有以下框架:


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)
    {
....
      }
    }
  }
}
  • 第 5 行:该类命名为 [MasterPage],并继承自系统类 [System.Web.UI.MasterPage]。
  • 第 9–14 行:6 个菜单选项作为该类的公共属性对外暴露
  • 第 16–19 行:public SetMenu 方法允许页面 [Formulaire.aspx、Simulations.aspx、Erreurs.aspx] 设置母版页菜单
  • 第 22–25 行:处理 [LinkButtonTerminerSession] 链接点击事件的程序
  • 第 28–31 行:处理母版页 Init 事件的程序

11.5.2.2. 类的公共属性


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; }
    }
 
...
  }
}

要理解这段代码,你需要回顾构成母版页的各个组件:

编号
类型
名称
角色
A
面板(上方的粉色部分)
页眉
页面页眉
B
面板(上图为黄色)
内容
页面内容
1
链接按钮
链接按钮运行模拟
请求模拟计算
2
链接按钮
LinkButtonClearSimulation
清除输入表单
3
链接按钮
LinkButtonViewSimulations
显示已执行的模拟列表
4
链接按钮
LinkButtonSimulationForm
返回输入表单
5
LinkButton
LinkButtonSaveSimulation
将当前模拟保存到模拟列表中
6
链接按钮
LinkButtonEndSession
结束当前会话

组件 1 至 6 在包含它们的页面之外不可访问。第 9 行至第 37 行中的属性旨在使其可供外部类访问,在本例中即应用程序其他页面的类。

11.5.2.3. SetMenu 方法

公共 SetMenu 方法允许页面 [Formulaire.aspx、Simulations.aspx、Erreurs.aspx] 设置母版页的菜单。其代码非常简单:


        // 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. 主页面上的事件处理

母版页将处理两个事件:

  • Init 事件,即页面生命周期中的第一个事件
  • [LinkButtonTerminerSession] 链接上的 Click 事件

母版页还有其他五个链接:[LinkButtonRunSimulation、LinkButtonSaveSimulation、LinkButtonClearSimulation、LinkButtonViewSimulations、LinkButtonSimulationForm]。以[LinkButtonRunSimulation]链接为例,让我们看看点击该链接时需要执行哪些操作:

  1. 验证 [Form.aspx] 页面上输入的数据(小时、天数)
  2. 计算薪资
  3. 在 [Form.aspx] 页面上显示结果

步骤 1 和 3 需要访问 [Form.aspx] 页面上的控件。但实际情况并非如此。事实上,母版页并不知道可能被插入到其 [ContentPlaceHolder1] 容器中的页面上有哪些控件。 在本示例中,[Formulaire.aspx] 页面需要处理 [LinkButtonFaireSimulation] 链接的点击事件,因为该事件发生时显示的正是该页面。它如何获知此事件?

  • 由于 [LinkButtonFaireSimulation] 链接不属于 [Formulaire.aspx] 页面,因此我们无法在 [Formulaire.aspx] 中编写常规的处理程序:

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

您可以在 [Formulaire.aspx] 中使用以下代码来解决此问题:


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)
    {
...
    }
 }
}
  • 第 12–15 行:当 [Formulaire.aspx] 页面的 Load 事件发生时,母版页的 [MasterPage] 类已被实例化。其 public Optionxx 属性可被访问,且类型为 LinkButton,这是一个支持 Click 事件的组件。我们将以下方法与这些 Click 事件关联:
    • OptFaireSimulation_Click 用于 LinkButtonFaireSimulation 链接的 Click 事件
    • OptEffacerSimulation_Click 用于 LinkButtonEffacerSimulation 链接的 Click 事件
    • OptVoirSimulations_Click:用于 LinkButtonVoirSimulations 链接的 Click 事件
    • OptEnregistrerSimulation_Click:用于 LinkButtonEnregistrerSimulation 链接的 Click 事件

六个菜单链接的点击事件处理将按以下方式分配:

  • [Formulaire.aspx] 页面将处理 [LinkButtonRunSimulation、LinkButtonSaveSimulation、LinkButtonClearSimulation、LinkButtonViewSimulations] 链接
  • [Simulations.aspx] 页面将处理 [LinkButtonSimulationForm] 链接
  • 母版页 [MasterPage.master] 将处理 [LinkButtonEndSession] 链接。对于此事件,它无需知道其封装的是哪个页面。

11.5.2.5. 母版页的 Init 事件

应用程序的三个页面 [Form.aspx、Simulations.aspx、Errors.aspx] 使用 [MasterPage.master] 作为其母版页。我们将母版页称为 M,被封装的页面称为 E。当客户端请求页面 E 时,将按以下顺序发生以下事件:

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

我们将利用页面 M 的 Init 事件来执行应尽早运行的代码,无论目标页面 E 是哪个。为了确定这段代码,让我们回顾一下应用程序概述:

上文中的 [Global] 是用于初始化应用程序的 [HttpApplication] 对象。该类与 [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)
    {
...
    }
  }
}

如果 [Global] 类无法正确初始化应用程序,它会设置两个静态公共变量:

  • 第 12 行中的布尔变量 Error 被设置为 true
  • 第 11 行上的 `Msg` 变量包含一条提供所遇错误详细信息的消息

当应用程序尚未正确初始化时,如果用户请求了 [Form.aspx] 或 [Simulations.aspx] 中的某个页面,该请求必须被转发或重定向至 [Errors.aspx] 页面,该页面将显示来自 [Global] 类的错误消息。处理这种情况有以下几种方法:

  • 在每个页面 [Formulaire.aspx、Simulations.aspx] 的 Init Load 事件处理程序中执行初始化错误检查
  • 在上述两个页面的母版页的 Init Load 事件处理程序中执行初始化错误检查。此方法的优势在于将初始化错误检查集中于单一位置。

我们选择在母版页Init 事件处理程序中执行初始化错误检查:


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

一旦请求了 [Form.aspx、Simulations.aspx、Errors.aspx] 中的任意一个页面,上述代码就会立即执行。如果请求的页面是 [Form.aspx] 或 [Simulations.aspx],我们只需(第 12 行)将客户端重定向到 [Errors.aspx] 页面,该页面会显示来自 [Global] 类的错误消息。 如果请求的页面是 [Errors.aspx],则不应发生此重定向:必须显示 [Errors.aspx] 页面。因此,我们需要在母版页的 [Page_Init] 方法中确定它所封装的是哪个页面。

让我们重新审视母版页的组件树:


...
<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>
  • 第 1-13 行:ID 为 "form1" 的容器
  • 第 4-6 行:ID 为 "entete" 的容器,包含在 ID 为 "form1" 的容器中
  • 第 8-11 行:ID 为 "content" 的容器,包含在 ID 为 "form1" 的容器中
  • 第 9-10 行:ID 为 "ContentPlaceHolder1" 的容器,包含在 ID 为 "content" 的容器中

嵌入主页面 M 中的页面 E 位于 ID 为 "ContentPlaceHolder1" 的容器内。若要引用该页面 E 上 ID 为 C 的组件,应编写如下代码:


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

[Errors.aspx] 页面的组件树如下:


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

当 [Errors.aspx] 页面与 M 母版页合并时,上述 <asp:Content> 标签(第 5–16 行)的内容会被整合到 M 页面上 ID 为 "ContentPlaceholder1" 的 <asp:ContentPlaceHolder> 标签中,从而形成以下组件树:

...
<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>
  • 第 12 行:[rptErrors] 组件可用于判断母版页 M 是否包含 [Errors.aspx] 页面。该组件仅存在于此页面上。

这些说明足以理解母版页中 [Page_Init] 过程的代码:


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;
            }
        }
  • 第 4 行:我们将一个事件处理程序与 LinkButtonTerminerSession 链接的 Click 事件关联起来。该处理程序位于 MasterPage 类中。
  • 第 6 行:我们检查 [Global] 类是否已设置其 Error 布尔值
  • 第 9 行:如果是,则 IsPageErrors 布尔变量表示主页面中封装的页面是否为 [Errors.aspx] 页面
  • 第 12 行:如果母版页中封装的页面不是 [Errors.aspx] 页面,则将客户端重定向到该页面;否则,不执行任何操作。

11.5.2.6. [LinkButtonTerminerSession] 链接的 Click 事件

当用户点击上文视图 (1) 中的 [End Session] 链接时,必须清除会话中的内容,并显示一个空表单 (2)。

该事件处理程序的代码如下:


        protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
        {
            // quit session 
            Session.Abandon();
            // the [form] view is displayed 
            Response.Redirect("Formulaire.aspx");
}
  • 第 4 行:当前会话被放弃
  • 第 6 行:客户端被重定向到 [Form.aspx] 页面

我们可以看到,这段代码并未涉及 [Form.aspx、Simulations.aspx、Errors.aspx] 页面中的任何组件。因此,该事件可由母版页本身进行处理。

11.5.3. [Errors.aspx] 页面的控件代码

[Errors.aspx] 页面的控件代码可以如下所示:


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);
    }
  }
}

请记住,[Errors.aspx] 页面的唯一作用是在发生应用程序初始化错误时显示该错误:

  • 第 10 行:检查初始化是否以错误结束
  • 第 13–14 行:如果是,则将错误消息 (Global.Msg) 放入列表 [InitializationErrors] 中
  • 第 16–17 行:指示 [rptErrors] 组件显示此列表
  • 第 20 行:无论是否发生错误,主页面菜单选项均不显示,因此用户无法从该页面发起任何新操作。

如果用户直接请求 [Errors.aspx] 页面(在应用程序的正常使用过程中,用户不应这样做),会发生什么情况?通过查看 [MasterPage.master.cs] 和 [Errors.aspx.cs] 中的代码,我们会发现:

  • 如果发生初始化错误,则显示该页面
  • 如果没有初始化错误,用户将收到一个仅包含 [MasterPage.master] 中页眉、且不显示任何菜单选项的页面。

11.5.4. [Formulaire.aspx] 页面的控件代码

11.5.4.1. 类骨架

[Form.aspx] 页面的控件代码骨架可能如下所示:


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

[Formulaire.aspx] 页面的控件代码处理五个事件:

  1. 页面的 Load 事件
  2. 母版页上 [LinkButtonRunSimulation] 链接的 Click 事件
  3. 母版页上 [LinkButtonClearSimulation] 链接的 Click 事件
  4. 母版页上 [LinkButtonEnregistrerSimulation] 链接的 Click 事件
  5. 母版页上 [LinkButtonViewSimulations] 链接的 Click 事件

11.5.4.2. 页面加载事件

页面加载事件处理程序的骨架代码如下:


    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 
....
        }
}

以下是一个用于阐明第17行注释的示例:

  • 在[1]中,我们要求查看模拟列表。已将条目记录在[A, B, C]中。
  • 在[2]中,我们查看该列表
  • 在[3]中,我们要求返回表单
  • 在[4]中,表单显示状态与离开时完全一致。由于存在两个请求,即(1,2)和(3,4),这意味着:
    • 从 [1] 跳转到 [2] 时,[1] 中的输入内容已被保存
    • 从 [3] 跳转到 [4] 时,这些数据被恢复。正是 [Form.aspx] 中的 [Page_Load] 过程执行了这一恢复操作。

问题:请参考 [pam-v4-3tier-nhibernate-multivues-monopage] 版本中的注释和代码,完成 Page_Load 过程


11.5.4.3. 处理菜单链接的点击事件

母版页上链接的点击事件处理程序的骨架如下:


// 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");
}

问题:请参考 [pam-v4-3tier-nhibernate-multivues-monopage] 版本中的注释和代码,完成上述过程的代码


11.5.5. [Simulations.aspx] 页面的控件代码

[Simulations.aspx] 页面的控件代码框架可如下所示:


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");
    }
 
}

问题:请参考 [pam-v4-3tier-nhibernate-multivues-monopage] 版本中的注释和代码,完成上述过程的代码


11.5.6. [Default.aspx] 页面的控件代码

您可以在应用程序中包含一个 [Default.aspx] 页面,以便用户在请求应用程序 URL 时无需指定具体页面,如下所示:

请求 [1] 收到了页面 [Formulaire.aspx] (2) 作为响应。我们知道请求 (1) 默认由应用程序的 [Default.aspx] 页面处理。要获得 (2),[Default.aspx] 只需将客户端重定向到 [Formulaire.aspx] 页面即可。这可以通过以下代码实现:


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");
    }
}

呈现页面 [Default.aspx] 仅包含将其与 [Default.aspx.cs] 关联的指令:


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