Skip to content

5. 应用程序 [SimuPaie] – 版本 2 – AJAX / ASP.NET

5.1. Introduction

AJAX(异步 JavaScript 和 XML)技术整合了一系列技术:

  • JavaScript:在浏览器中显示的 HTML 页面可嵌入 JavaScript 代码。 如果用户未在浏览器中禁用 JavaScript 代码的执行,该代码将由浏览器执行。该技术自万维网诞生之初便已出现,并自 2005 年起因 AJAX 而重新受到关注。
  • DOM:文档对象模型(Document Object Model)。嵌入在HTML页面中的JavaScript代码,能够以对象树的形式访问该文档,即DOM。 文档中的每个元素(输入字段、命名过的 <div> 标签、<select> 标签等)都由树中的一个节点表示。 JavaScript 代码可以通过修改 DOM 来更改显示的文档。例如,它可以通过代表该输入字段的 DOM 节点,来更改输入字段中显示的文本。

AJAX 使用 JavaScript 在后台进行浏览器与服务器之间的交互。 当发生点击按钮等事件时,浏览器将执行 JavaScript 代码,向服务器发送 HTTP 请求。该请求将包含指示服务器执行操作的参数,服务器随后将执行所请求的操作。 服务器将向浏览器发送一个名为 HTTP 的数据流作为响应,其中包含用于部分更新当前显示页面的数据。该更新将通过文档中的 DOM 以及文档中嵌入的 JavaScript 代码的执行来实现。 服务器返回的数据可以采用多种格式:XML、HTML、JSON、未格式化文本等。

该技术的优势在于对显示文档进行部分更新。这意味着:

  • 客户端与服务器之间传输的数据更少,响应速度因此更快
  • 图形界面操作更流畅,因为用户的操作不会导致页面完全重新加载。

在网络传输速度较快的内联网环境中,AJAX 能够创建行为模式接近传统 Windows 界面的 Web 应用程序。实际上,我们可以处理诸如列表中选项变更等事件,并立即对页面进行部分刷新以反映该选项变更。 页面无需完全重新加载,这让用户获得了与 Windows 应用程序相似的流畅体验。对于响应时间可能较长的互联网应用程序,AJAX 技术依然适用,但流畅度体验取决于网络状况。

AJAX的主要难点在于JavaScript语言。该语言诞生于万维网的早期,

  • 但它并不适合进行面向对象的编程。例如,它并非一种类型化语言,无需声明所处理数据的类型,这使其更接近Perl或PHP等语言。
  • 它没有被所有浏览器普遍接受的标准。每个浏览器都有其专有的“JavaScript”扩展,这导致为 IE 编写的 JavaScript 代码可能无法在 Mozilla Firefox 中运行,反之亦然。
  • 它难以调试,因为浏览器并未提供高效的工具来调试其执行的JavaScript代码。

正是由于JavaScript存在这些缺陷,在AJAX出现之前,它鲜少被使用。一旦人们意识到在后台执行JavaScript代码的价值——即向Web服务器发送HTTP请求,并利用服务器的响应来实现 借助DOM实现显示文档的部分更新,开发团队便着手投入工作,并推出了无需开发者编写JavaScript代码即可实现AJAX功能的框架。 为此,开发了能够适配客户端浏览器的JavaScript库。将JavaScript代码插入发送至浏览器的HTML页面是在服务器端完成的,具体技术实现因所使用的AJAX框架而异。 目前已有适用于 Java、.NET、PHP、Ruby 等语言的 Ajax 框架。AJAX 已集成于 Visual Web Developer 2008 中。

5.2. [web-ui-ajax] 层的 Visual Web Developer 项目

新版本是通过复制旧版本获得的。仅对 [Default.aspx] 页面进行修改。

  • 在 [1] 中,使用 Windows 资源管理器复制 [pam-v1-adonet] 项目文件夹,并将其重命名为 [pam-v2-adonet-ajax]。 然后使用 Visual Web Developer,打开该新文件夹 [2] 中的项目(文件 / 打开项目)
  • ,将其重命名为
  • 在新项目的属性中,将程序集名称 [4] 以及默认命名空间 [5] 进行修改。

完成上述操作后,在文件 [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs, Global.asax, Global.asax.cs] 中,将命名空间 [pam_v1] 替换为 [pam_v2],以与默认命名空间保持一致。 例如,[Default.aspx] [6] 变为:


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

页面 [Default.aspx] 将按以下方式修改:

  • 在表单 [Default.aspx] 中添加 Ajax 组件(参见上文的 [2])。
    • 2A:所有 AJAX 项目均需使用 <asp:ScriptManager> 组件
    • 2B:<asp:UpdatePanel>组件用于在用户执行POST操作时限定需要刷新区域。该组件可避免页面完全重新加载。
    • 2C:<asp:UpdateProgress> 组件用于在更新期间显示文本或图像,以提醒用户,如下所示:
  • (续)
    • 2D:一个名为 LabelHeureLoad 的 Label 类型,用于显示页面事件处理程序 Load 的执行时间。
    • 2E: 一个名为 LabelHeureSalaireLabel 类型事件,用于显示 [Salaire] 按钮上 Click 事件处理程序的执行时间。 我们希望展示的是,点击按钮 [Salaire] 时,Ajax 不会重新加载整个页面。前面的屏幕截图展示了这一点,其中两个 Labels 显示了两个不同的时间。

之前对表单的 Ajax 化处理可直接在 [Default.aspx] 的源代码中完成,方法是添加 Ajax 扩展标签:


...
<body style="text-align: left" bgcolor="#ffccff">
  <h3>
    Feuille de salaire
    <asp:Label ID="LabelHeureLoad" runat="server" BackColor="#C0C000"></asp:Label></h3>
  <hr />
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
    <asp:UpdatePanel runat="server" ID="UpdatePanelPam" UpdateMode="Conditional">
      <ContentTemplate>
        <div>
          <table>
            <tr>
...
            <tr>
              <td>
                <asp:DropDownList ID="ComboBoxEmployes" runat="server">
                </asp:DropDownList></td>
              <td>
                <asp:TextBox ID="TextBoxHeures" runat="server" EnableViewState="False">
                </asp:TextBox></td>
              <td>
                <asp:TextBox ID="TextBoxJours" runat="server" EnableViewState="False">
                </asp:TextBox></td>
              <td>
                <asp:Button ID="ButtonSalaire" runat="server" Text="Salaire" CausesValidation="False" /></td>
                <td>   <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>
          <asp:Label ID="LabelHeureSalaire" runat="server" BackColor="#C0C000"></asp:Label></td>
            </tr>
            <tr>
...
            </tr>
          </table>
        </div>
        <br />
....
          <table>
            <tr>
              <td>
                Salaire net à payer :
              </td>
              <td align="center" bgcolor="#C0C000" height="20px">
                <asp:Label ID="LabelSN" runat="server" EnableViewState="False"></asp:Label></td>
            </tr>
          </table>
          &nbsp;</asp:Panel>
      </ContentTemplate>
    </asp:UpdatePanel>
  </form>
</body>
</html>
  • 第 8 行:所有经过 Ajax 化的表单都必须包含 <asp:ScriptManager> 标签。正是这个标签使得新 Ajax 组件得以使用:
  • 双击上方的 [ScriptManager] 组件即可获得 <asp:ScriptManager> 标签。 请务必确认该标签位于源代码的 <form> 标签内。第 8 行中的 [EnablePartialRendering= "true "] 属性默认缺失。由于其默认值为 "true",因此并非必需。
  • 第 9 行:<asp:UpdatePanel> 标签用于在页面部分刷新时,限定需要更新的页面区域。 [UpdateMode="Conditional"] 属性表示该区域仅在区域内某个组件触发 AJAX 事件时才需更新。另一个属性值为 [UpdateMode="Always"],这是默认值。 使用此属性时,即使发生的 Ajax 事件源自另一个区域 UpdatePanel 的组件,区域 UpdatePanel 仍会系统性地被更新。通常情况下,这种行为是不希望出现的。

<asp:UpdatePanel> 标签支持两个子标签:<ContentTemplate> 和 <Triggers>。

  • 第 10 行和第 53 行的 <asp:ContentTemplate> 标签界定了页面中需要进行部分更新的区域。
  • 第 27-33 行:一个 Ajax 组件 <asp:UpdateProgress>,用于在页面更新期间显示文本。 例如,点击 [Salaire] 按钮将触发后台的 POST 操作。此时浏览器不会显示沙漏图标,用户因此可能误以为可以继续使用表单。 <asp:UpdateProgress> 标签可显示一条提示文本,告知页面正在更新。也可以显示一张图片。在此,我们显示了一张动画图片(第 29 行)以及一段文本(第 30-31 行):
 
  • 第28-32行:<ProgressTemplate>标签界定了在UpdatePanel区域更新期间将显示的内容,该区域内包含UpdateProgress标签
  • 第29-31行:在UpdatePanel区域更新期间将显示的动画图像和文本。
  • 第 5 行:标签 LabelHeureLoad 被放置在 AJAX 区域之外
  • 第 34 行:标签 LabelHeureSalaire 被放置在 AJAX 区域内

页面 [Default.aspx.cs]( )的代码则按以下方式进行调整:


    protected void Page_Load(object sender, System.EventArgs e)
    {
        // 每次页面加载的时间
        LabelHeureLoad.Text = DateTime.Now.ToString("hh:mm:ss");
        // 初始请求处理
        if (!IsPostBack)
        {
....
}
  • 第 4 行:更新页面加载时间

   protected void ButtonSalaire_Click(object sender, System.EventArgs e)
    {
        // 工资计算时间
        LabelHeureSalaire.Text = DateTime.Now.ToString("hh:mm:ss");
        // 正在验证数据
....
}
  • 第 4 行:更新工资计算时间

要完成页面 [Default.aspx],我们需要包含由该页面源代码第 4 行引用的动画图片:


            <td>
              <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>
              <asp:Label ID="LabelHeureSalaire" runat="server" BackColor="#C0C000"></asp:Label>
</td>

使用 Windows 资源管理器,我们将文件 [images/indicator.gif] 添加到项目文件夹 [1] 中:

  • 在 [2] 中,显示项目中的所有文件
  • 在 [3] 中,文件夹 [images]
  • 在 [4] 中,将文件夹 [images] 纳入项目
  • 转换为 [5],文件夹 [images] 已包含在项目中
  • 在 [6] 中,隐藏不属于该项目的文件
  • 在 [7] 中,新项目

5.3. Ajax 解决方案测试

当请求运行项目(CTRL-F5)时,将显示以下页面:

  • 在 [1] 中,页面加载时间

请进行测试,并确认按钮 [Salaire] 不会导致页面完全重新加载。 您可以通过 [Salaire] 和 [2] 按钮点击处理时间(显示的时间)来观察这一点,该时间与页面 [3] 的初始加载时间并不相同:

将组件 ScriptManager1 的属性 EnablePartialRendering 设置为 False 后重新进行测试:

  

此时可观察到页面表现为非Ajax模式。点击按钮[Salaire]时,页面会完全重新加载。请使用其他浏览器重新进行测试。 进行多浏览器测试旨在验证由服务器组件 ASP / AJAX 生成的 JavaScript 能否被不同浏览器正确解析。

最后,让我们重点说明 <asp:UpdateProgress> 标签的作用。在 [Default.aspx.cs] 的 [ButtonSalaire_Click] 存储过程代码中,添加一条将存储过程暂停 3 秒的语句:


using System.Threading;
   ...
protected void ButtonSalaire_Click(object sender, System.EventArgs e)
    {
        // 工资计算时间
        LabelHeureSalaire.Text = DateTime.Now.ToString("hh:mm:ss");
        // 等待 
        Thread.Sleep(3000);
        // 正在验证数据
....
}

第 8 行使执行 [ButtonSalaire_Click] 过程的线程等待 5 秒。 完成上述操作后,重新进行测试(在将组件 ScriptManager1 的属性 EnablePartialRendering 设为 True 之后)。这次,在计算工资期间,我们可以看到 [UpdateProgress] 的文本以及动画图像:

5.4. Conclusion

前文的研究表明,现有 ASP.NET 应用程序是可以实现 AJAX 化的。 AJAX 的扩展功能比刚才所见的内容更进一步。欢迎读者访问 [http://ajax.asp.net] 网站。