Skip to content

8. [SimuPaie] 应用程序 – 版本 4 – ASP.NET / 多视图 / 单页


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

  • 服务器组件与应用程序控制器

  • 基于 ASP 服务器组件的 MVC 应用程序示例


接下来,我们将探讨之前讨论过的三层 ASP.NET 应用程序的一个衍生版本,该版本增加了新功能。我们的应用程序架构演变如下:

客户端请求的处理流程如下:

  1. 客户端向应用程序发出请求。
  2. 应用程序处理该请求。为此,它可能需要[业务]层的协助,而[业务]层若需与数据库交换数据,则可能需要[DAO]层的协助。应用程序从[业务]层接收响应。
  3. 基于该响应,它选择 (3) 要发送给客户端的视图(即响应),并向客户端提供 (4) 其所需的信息(模型)。
  4. 响应被发送给客户端(5)

这是一种被称为 MVC(模型–视图–控制器)的 Web 架构:

  • [应用程序]是控制器。它处理所有客户端请求。
  • [输入、模拟、模拟结果等] 属于视图。在 .NET 中,视图是标准的 ASP/HTML 代码,其中包含需要初始化的组件。必须提供给这些组件的值构成了视图的模型

在此,我们将按以下方式实现 MVC 设计模式:

  • 视图将作为 [View] 组件位于单页 [Default.aspx] 中
  • 控制器则是该单页的 [Default.aspx.cs] 代码。

只有基础应用程序才能支持这种 MVC 实现。实际上,每次请求时,[Default.aspx] 页面的所有组件都会被实例化,即所有视图。 在发送响应时,应用程序的控制代码会通过简单地将相应的 [View] 组件设为可见并隐藏其余组件,来选定其中一个视图。如果应用程序拥有大量视图,[Default.aspx] 页面将包含大量组件,而实例化它们的开销可能会变得难以承受。 此外,由于视图过多,页面的 [Design] 模式可能会变得难以管理。这种架构适用于视图较少且由单人开发的应用程序。当可以采用这种架构时,它能非常简单地实现 MVC 架构的开发。这正是我们将在新版本中探讨的内容。

8.1. 应用程序的视图

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

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

Image

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

Image

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

Image

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

Image

  • [ErrorView] 视图,表示存在一个或多个错误:

Image

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

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

  • 在 [1] 中我们发现:
  • 应用程序的配置文件 [Web.config] 与前一个应用程序的配置文件完全相同。
  • 处理 Web 应用程序事件的 [Global.cs] 文件(在此情况下处理其启动)与前一个应用程序的完全相同,只是它还处理用户会话的启动。
  • 应用程序的 [Default.aspx] 表单——包含应用程序的各种视图。
  • 在 [2] 中是项目引用——它们与上一版本的完全相同

8.3. [Global.cs] 文件

负责处理 Web 应用程序事件的 [Global.cs] 文件与上一版应用程序中的完全相同,唯一的区别在于它还处理用户会话的启动:

Global.cs


using System;
using System.Web;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;
using System.Collections.Generic;
using istia.st.pam.web;
 
namespace pam_v4
{
    public class Global : HttpApplication
    {
 
        // --- static application data ---
        public static Employe[] Employes;
        public static string Msg = string.Empty;
        public static bool Erreur = false;
        public static IPamMetier PamMetier = null;
 
        // application startup
        public void Application_Start(object sender, EventArgs e)
        {
...
        }
 
        // start user session 
        public void Session_Start(object sender, EventArgs e)
        {
            // put an empty simulation list in the session 
            Session["simulations"] = new List<Simulation>();
        } 
 
    }
}
  • 第 27–34 行:我们处理会话的开始。我们将把用户执行的模拟列表放入此会话中。
  • 第 30 行:创建一个空的模拟列表。模拟是一个 [Simulation] 类型的对象,我们稍后将详细描述。
  • 第 31 行:将模拟列表放入与“simulations”键关联的会话中

8.4. [Simulation] 类

[Simulation] 类型的对象用于封装模拟表中的一行:

Image

其代码如下:


namespace Pam.Web
{
 
    public class Simulation
    {
        // simulation data 
        public string Nom { get; set; }
        public string Prenom { get; set; }
        public double HeuresTravaillees { get; set; }
        public int JoursTravailles { get; set; }
        public double SalaireBase { get; set; }
        public double Indemnites { get; set; }
        public double CotisationsSociales { get; set; }
        public double SalaireNet { get; set; }
 
        // manufacturers 
        public Simulation()
        {
 
        }
 
        public Simulation(string nom, string prenom, double heuresTravailllees, int joursTravailles, double salaireBase, double indemnites, double cotisationsSociales, double salaireNet)
        {
            {
                this.Nom = nom;
                this.Prenom = prenom;
                this.HeuresTravaillees = heuresTravailllees;
                this.JoursTravailles = joursTravailles;
                this.SalaireBase = salaireBase;
                this.Indemnites = indemnites;
                this.CotisationsSociales = cotisationsSociales;
                this.SalaireNet = salaireNet;
            }
        }
    }
}

该类的字段对应于模拟表的列。

8.5. [Default.aspx] 页面

8.5.1. 概述

[Default.aspx] 页面包含多个 [View] 组件,每个视图对应一个组件。其框架结构如下:


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="pam_v4.PagePam" %>
 
<!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>Simulateur de paie</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>
            <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 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" OnClick="LinkButtonFaireSimulation_Click">
                                    | Faire la simulation<br /></asp:LinkButton>
                       <asp:LinkButton ID="LinkButtonEffacerSimulation" runat="server"
                                     CausesValidation="False" OnClick="LinkButtonEffacerSimulation_Click">
                                    | Effacer la simulation<br /></asp:LinkButton>
                       <asp:LinkButton ID="LinkButtonVoirSimulations" runat="server" 
                                CausesValidation="False" OnClick="LinkButtonVoirSimulations_Click">
                                    | Voir les simulations<br /></asp:LinkButton>
                        <asp:LinkButton ID="LinkButtonFormulaireSimulation" runat="server"
                                CausesValidation="False" OnClick="LinkButtonFormulaireSimulation_Click">
                                | Retour au formulaire de simulation<br /></asp:LinkButton>
                        <asp:LinkButton ID="LinkButtonEnregistrerSimulation" runat="server"
                                CausesValidation="False" OnClick="LinkButtonEnregistrerSimulation_Click">
                                    | Enregistrer la simulation<br /></asp:LinkButton>
                        <asp:LinkButton ID="LinkButtonTerminerSession" runat="server"
                                CausesValidation="False" OnClick="LinkButtonTerminerSession_Click">
                                    | Terminer la session<br /></asp:LinkButton>
                    </td>
            </table>
            <hr />
            <asp:MultiView ID="Vues1" ActiveViewIndex="0" runat="server">
                <asp:View ID="VueSaisies" runat="server">
...
                </asp:View>
            </asp:MultiView>
            <asp:MultiView ID="Vues2" runat="server">
                <asp:View ID="VueSimulation" runat="server">
...
                </asp:View>
                <asp:View ID="VueSimulations" runat="server">
...
                </asp:View>
                <asp:View ID="VueSimulationsVides" runat="server">
...
                </asp:View>
                <asp:View ID="VueErreurs" runat="server">
...
                </asp:View>
            </asp:MultiView>
        </ContentTemplate>
    </asp:UpdatePanel>
    </form>
</body>
</html>
  • 第 10 行:用于启用 Ajax 扩展的标签
  • 第 11–73 行:由 Ajax 调用更新的 UpdatePanel 容器
  • 第 12–72 行:UpdatePanel 的内容
  • 第 13–52 行:将在每个视图中显示的标题。它以链接列表的形式向用户展示可执行的操作。
  • 第 33 行:请注意 CausesValidation="False" 属性,它确保在点击链接时不会隐式执行页面验证器。若省略此属性,其默认值为 True。可通过在服务器端代码中使用 Page.Validate 操作显式执行页面验证。
  • 第 53–57 行:一个包含单个视图 [VueSaisies] 的 [MultiView] 组件。因此,第 53 行中硬编码了要显示的视图编号:ActiveViewIndex="0"
  • 第 53–67 行: 一个包含四个视图的 [MultiView] 组件:[VueSimulation] 视图(第 59–61 行)、[VueSimulations] 视图(第 62–64 行)、[VueSimulationsVides] 视图(第 65–67 行)以及 [VueErreurs] 视图(第 68–70 行)。 [MultiView] 组件每次仅显示一个视图。要显示 Vues2 组件的第 i 个视图,请编写以下代码:
Vues2.ActiveViewIndex=i

8.5.2. en 页眉

该标头由以下组件构成:

编号
类型
名称
角色
1
链接按钮
LinkButtonRunSimulation
请求进行模拟计算
2
链接按钮
LinkButtonClearSimulation
清除输入表单
3
链接按钮
LinkButtonViewSimulations
显示已执行的模拟列表
4
链接按钮
LinkButtonSimulationForm
返回输入表单
5
LinkButton
LinkButtonSaveSimulation
将当前模拟保存到模拟列表中
6
链接按钮
LinkButtonEndSession
结束当前会话

8.5.3. [ Saisies]视图

名为 [VueSaisies] 的 [View] 组件如下所示:

编号
类型
名称
角色
1
下拉列表
员工下拉列表
包含员工姓名列表
2
文本框
TextBoxHours
实际工作小时数
3
文本框
TextBoxDays
工作天数 – 整数
4
必填字段验证器
必填字段验证器(小时)
检查字段 [2] [TextBoxHours] 是否不为空
5
正则表达式验证器
正则表达式验证器(小时)
检查 [2] [TextBoxHours] 字段是否为大于等于 0 的实数
6
必填字段验证器
必填字段验证器(天数)
检查 [3] [TextBoxDays] 字段是否不为空
7
正则表达式验证器
正则表达式验证器(天数)
检查 [3] [TextBoxDays] 字段是否为大于等于 0 的整数

8.5.4. 名为 [Simulati on] 的视图

名为 [SimulationView] 的 [View] 组件如下所示:

Image

它仅由上述 ID 对应的 [Label] 组件构成。

8.5.5. [Simu lations]视图

名为 [SimulationView] 的 [View] 组件如下所示:

编号
类型
名称
角色
1
GridView
GridViewSimulations
包含模拟列表

[GridViewSimulations] 组件的属性定义如下:

  • 在 [1] 中:右键单击 [GridView] / [自动格式] 选项
  • 在 [2] 中:为 [GridView] 选择显示类型
  • 在 [3] 中:选择 [GridView] 的属性
  • 在 [4] 中:编辑 [GridView] 的列
  • 在 [5] 中:添加一个 [BoundField] 列,将其绑定到要显示在 [GridView] 行中的对象的某个公共属性。此处显示的对象将是一个 [Simulation] 对象。
  • 在 [6] 中:输入列标题
  • 在 [7] 中:指定将与该列关联的 [Simulation] 类的属性名称。
  • [DataFormatString] 指定了该列中显示的值应采用何种格式。

[GridViewSimulations] 组件的列具有以下属性:

编号
属性
2
类型:BoundField,标题文本:姓名,数据字段:姓名
3
类型:绑定字段,标题文本:姓氏,数据字段:姓氏
4
类型:绑定字段,标题文本:工作小时数,数据字段:工作小时数
5
类型:绑定字段,标题文本:工作日数,数据字段:DaysWorked
6
类型:BoundField,标题文本:基本工资,数据字段:BaseSalary,数据格式字符串:{0:C}(货币格式,C=货币)——将显示欧元符号。
7
类型:绑定字段,标题文本:津贴,数据字段:Indemnites,数据格式字符串:{0:C}
8
类型:BoundField,标题文本:社会保障缴费,数据字段:SocialSecurityContributions,数据格式字符串:{0:C}
9
类型:绑定字段,标题文本:净工资,数据字段:NetSalary,数据格式字符串:{0:C}

请注意,[DataField] 必须与 [Simulation] 类的现有属性相对应。本阶段结束时,所有类型为 [BoundField] 的列均已创建:

  • 在 [1] 中:为 [GridView] 创建的列
  • 在 [2] 中:当开发人员像我们刚才那样手动定义列时,必须禁用自动列生成功能。

我们还需要创建 [Remove] 链接列:

Image

  • 在 [1] 中:添加一个类型为 [CommandField / Delete] 的列
  • 在 [2] 中:将 ButtonType 设为 Link,使该列显示链接而非按钮
  • 在 [3] 中:CausesValidation=False;点击链接不会触发页面上可能存在的任何验证检查。事实上,删除模拟并不需要任何数据验证。
  • 在 [4] 中:仅显示删除链接。
  • 在 [5] 中:此链接的文本

8.5.6. [Simulation sVides]视图

名为 [VueSimulationsVides] 的 [View] 组件仅包含文本:

 

8.5.7. [E rrors] 视图

名为 [VueErreurs] 的 [View] 组件如下所示:

编号
类型
名称
角色
1
中继器
RptErrors
显示错误消息列表

[Repeater] 组件允许您针对数据源中的每个对象(通常是集合)重复执行 ASP.NET/HTML 代码。该代码直接定义在页面的 ASP.NET 源代码中:


                <asp:Repeater ID="RptErreurs" runat="server">
                    <ItemTemplate>
                        <li>
                            <%# Container.DataItem %>
                        </li>
                    </ItemTemplate>
</asp:Repeater>
  • 第 2 行:<ItemTemplate> 定义了将在数据源中的每个项目上重复执行的代码。
  • 第 4 行:显示 Container.DataItem 表达式的值,该表达式指代数据源中的当前元素。由于该元素是一个对象,因此使用该对象的 ToString 方法将其包含在页面的 HTML 输出中。我们的对象集合将是一个包含错误消息的 List(Of String) 集合。第 3–5 行将在页面的 HTML 输出中包含 <li>Message</li> 序列。

8.6. 控制器 [Default.aspx.cs]

8.6.1. 概述

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

  • [Default.aspx.cs] 是单页 [Default.aspx] 的代码,它充当应用程序控制器。
  • [Global] 是 [HttpApplication] 对象,它负责初始化应用程序,并引用了 [业务] 层。

[Default.aspx.cs] 控制器代码的框架如下:


using System.Collections.Generic;
...
 
public partial class PagePam : Page
{
 
    private void setVues(bool boolVues1, bool boolVues2, int index)
    {
        // display the requested views 
        // boolVues1 : true if Vues1 multi-view is to be visible 
        // boolVues1 : true if the Vues2 multiview is to be visible 
        // index: index of the Vues2 view to be displayed 
...
    }
 
    private void setMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
    {
        // set menu options 
        // each Boolean is assigned to the Visible property of the corresponding link 
...
    }
 
    // page loading 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // initial request processing 
        if (!IsPostBack)
        {
...
        }
    }
 
    protected void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 
    protected void LinkButtonEffacerSimulation_Click(object sender, System.EventArgs e)
    {
....
    }
 
    protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
    {
...
    }
 
    protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 
    protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
    {
...
    }
 
    protected void LinkButtonFormulaireSimulation_Click(object sender, System.EventArgs e)
    {
...
    }
 
 
    protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
    {
...
    }
}

针对用户的初始请求(GET),仅处理第 24–31 行中的 Load 事件。对于通过菜单链接发起的后续请求(POST),将处理两个事件:

  1. Load 事件(第 24–31 行),但第 27 行对 Page.IsPostback 的布尔检查确保不会执行任何操作。
  2. 与被点击的链接关联的事件:
  • 第 33–36 行:处理链接 [1] 的点击
  • 第 38–41 行:处理链接 [2] 的点击
  • 第 43–46 行:处理链接 [3] 的点击事件
  • 第 58–61 行:处理链接 [4] 的点击事件
  • 第 48–51 行:处理链接 [5] 的点击
  • 第 53–56 行:处理链接 [6] 的点击事件

为了提取频繁重复的代码片段,已创建了两个内部方法:

  • setVues,第 7–14 行:设置要显示的视图
  • setMenu,第 16–21 行:设置要显示的菜单选项

8.6.2. Load 事件


推荐阅读:参考资料 [1],《ASP.NET 1.1 Web 开发》:

  • 关于 [Repeater] 组件和数据绑定的章节。

用户最初看到的视图是空表单:

Image

初始化应用程序需要访问数据源,此过程可能会失败。在这种情况下,显示的首个页面将是错误页面:

Image

[Load] 事件的处理方式与之前的 ASP.NET 版本类似:


    // page loading 
    protected void Page_Load(object sender, System.EventArgs e)
    {
        // initial request processing 
        if (!IsPostBack)
        {
            // initialization errors? 
            if (Global.Erreur)
            {
                // display view [errors] 
...
                // menu positioning 
                ...
                return;
            }
            // loading employee names into the combo 
...
            // menu positioning 
...
            // view display [input] 
 ...
        }
}

问题:请补全上面的代码


8.6.3. 操作:运行模拟

下文中的屏幕 (1) 是用户的请求,屏幕 (2) 是 Web 应用程序发送给用户的响应。用户可以在主屏幕上启动模拟:

Image

- (1):用户请求进行模拟

Image

- (2):模拟结果

处理此操作的流程可能如下所示:


    protected void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
    {
        // wage calculation 
        // valid page? 
        Page.Validate();
        if (!Page.IsValid)
        {
            // view display [input] 
            ...
            return;
        }
        // the page is validated - inputs are retrieved 
        double HeuresTravaillées = ...;
        int JoursTravaillés = ...;
        // we calculate the employee's salary 
        FeuilleSalaire feuillesalaire;
        try
        {
            feuillesalaire = ...
        }
        catch (PamException ex)
        {
            // display view [errors] 
...
            return;
        }
        // put the result in the session 
        Session["simulation"] = ...
        // displaying results 
...
        // display views [entry, employee, salary] 
        ...
        // menu display 
        ...
}

问题:请补全上面的代码


8.6.4. 操作:保存模拟

模拟完成后,用户可以请求保存它:

- (1):请求保存当前模拟

Image

- (2):模拟已保存,并显示已完成的模拟列表

处理此操作的流程可能如下所示:


    protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
    {
        // save the current simulation in the list of simulations in the session 
        ...
        // the [simulations] view is displayed 
        ...
}

问题:请补全上述代码


8.6.5. 操作:返回模拟表单


推荐阅读:参考文献 [1],《使用 ASP.NET 1.1 进行 Web 开发》:隐藏字段 _VIEWSTATE 的作用


一旦显示了模拟列表,用户可以请求返回模拟表单:

请注意,屏幕 (2) 显示的是表单被提交时的状态。这里需要特别注意的是,这些不同的视图都属于同一个页面。在请求之间,如果控件的 EnableViewState 属性设置为 true,则控件的值会通过 ViewState 机制进行保存。

处理此操作的程序可能如下所示:


    protected void LinkButtonFormulaireSimulation_Click(object sender, System.EventArgs e)
    {
        // view display [input] 
        ...
        // menu positioning 
        ...
}

问题:完成上面的代码


8.6.6. 操作:清除模拟

返回模拟表单后,用户可以请求清除当前输入的内容:

处理此操作的流程可能如下所示:


    protected void LinkButtonEffacerSimulation_Click(object sender, System.EventArgs e)
    {
        // RAZ of the form 
        ...
}

问题:完成上述代码


8.6.7. 操作:查看模拟

用户可以请求查看他们已经创建的模拟:

处理此操作的程序可能如下所示:


protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
    {
        // simulations are retrieved from the
        ...
        // are there any simulations? 
        if (...)
        {
            // view [simulations] visible 
...
        }
        else
        {
            // view [SimulationsVides] 
            ...
        }
        // set the menu 
        ...
    }

问题:完成上述代码


8.6.8. 操作:删除模拟

用户可以请求删除一个模拟:

处理此操作的 [GridViewSimulations_RowDeleting] 过程可能如下所示:


    protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
    {
        // simulations are retrieved from the
        ...
        // delete the designated simulation (e.RowIndex is the number of the deleted line)
        ...
        // are there any simulations left? 
        if (...)
        {
            // fill in the GridView 
 ...
        }
        else
        {
            // view [SimulationsVides] 
            ...
        }
}

问题:完成上面的代码


8.6.9. 操作:结束会话

用户可以请求结束其模拟会话。这将丢弃会话内容并显示一个空表单:

处理此操作的程序可能如下所示:


    protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
    {
        // quit session 
        ...
        // display [entries] view 
        ...
        // menu positioning 
        ...
}

问题:请补全上面的代码


实践练习:在您的机器上实现前面的 Web 应用程序。为其添加 Ajax 功能。