作者 :Scott Cate

也许 ASP.NET AJAX 扩展最明显的功能是能够在不对服务器执行完全回发的情况下执行部分或增量页面更新,而无需更改代码和最少的标记更改。 优点是广泛的 - 多媒体 ((如 Adobe Flash 或 Windows Media) )的状态保持不变,带宽成本会降低,并且客户端通常不会遇到与回发相关的闪烁。

Microsoft 的 ASP.NET 技术带来了面向对象的事件驱动编程模型,并结合编译代码的优势。 但是,其服务器端处理模型在技术中固有几个缺点:

  • 页面更新需要往返服务器,这需要页面刷新。
  • 往返不会保留 Javascript 或其他客户端技术 ((如 Adobe Flash) )生成的任何效果
  • 回发期间,Microsoft Internet Explorer 以外的浏览器不支持自动还原滚动位置。 即使在 Internet Explorer 中,在刷新页面时仍会闪烁。
  • 回发可能涉及大量带宽,因为__VIEWSTATE窗体字段可能会增长,尤其是在处理 GridView 控件或重复器等控件时。
  • 没有统一的模型可用于通过 JavaScript 或其他客户端技术访问 Web 服务。
  • 输入 Microsoft ASP.NET AJAX 扩展。 AJAX 代表 A 同步 J avaScript A nd X ML,是一个集成框架,用于通过跨平台 JavaScript 提供增量页面更新,由由 Microsoft AJAX 框架组成的服务器端代码组成,以及名为 Microsoft AJAX 脚本库的脚本组件。 ASP.NET AJAX 扩展还提供跨平台支持,用于通过 JavaScript 访问 ASP.NET Web 服务。

    本白皮书检查 ASP.NET AJAX 扩展的部分页面更新功能,其中包括 ScriptManager 组件、UpdatePanel 控件和 UpdateProgress 控件,并考虑应或不应使用的方案。

    此白皮书基于 Visual Studio 2008 的 Beta 2 版本和 .NET Framework 3.5,该版本将 ASP.NET AJAX 扩展集成到基类库中, (该库以前是可用于 ASP.NET 2.0) 的附加组件。 本白皮书还假定你使用的是 Visual Studio 2008,而不是 Visual Web Developer Express Edition;某些引用的项目模板可能不适用于 Visual Web Developer Express 用户。

    也许 ASP.NET AJAX 扩展最明显的功能是能够在不对服务器执行完全回发的情况下执行部分或增量页面更新,而无需更改代码和最少的标记更改。 优点是广泛的 - 多媒体 ((如 Adobe Flash 或 Windows Media) )的状态保持不变,带宽成本会降低,并且客户端通常不会遇到与回发相关的闪烁。

    集成部分页面呈现的功能集成到 ASP.NET 中,对项目所做的更改最少。

    演练:将部分呈现集成到现有项目中

  • 在 Microsoft Visual Studio 2008 中,转到 “文件 -> 新建 -> 网站 ”,并从对话框中选择“ASP.NET 网站”,创建新的 ASP.NET 网站项目。 你可以将其命名为喜欢的任何内容,也可以将其安装到文件系统或 Internet Information Services (IIS) 。
  • 将显示空白默认页面,其中基本 ASP.NET 标记 (服务器端窗体和 @Page 指令) 。 删除名为 Label1 标签的标签,并将一个按钮拖 Button1 到窗体元素中的页面上。 你可以将其文本属性设置为喜欢的任何内容。
  • 在“设计” Button1 视图中,双击以生成代码隐藏事件处理程序。 在此事件处理程序中,设置为 Label1.Text “你单击了该按钮! .
  • 列表 1:启用部分呈现之前 default.aspx 的标记

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
    <!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>
     </head>
     <form id="form1" runat="server">
     <asp:Label ID="Label1" runat="server" Text="This is a label!"></asp:Label>
     <asp:Button ID="Button1" runat="server" Text="Click Me" OnClick="Button1_Click" />
     </form>
     </body>
    </html>
    

    列表 2:default.aspx.cs 中剪裁的代码隐藏 ()

    public partial class _Default : System.Web.UI.Page
        protected void Button1_Click(object sender, EventArgs e)
            Label1.Text = "You clicked the button!";
    
  • 按 F5 启动网站。 Visual Studio 将提示你添加web.config文件以启用调试;这样做。 单击该按钮时,请注意页面会刷新以更改标签中的文本,并且重新绘制页面时会短暂闪烁。
  • 关闭浏览器窗口后,返回到 Visual Studio 和标记页。 在 Visual Studio 工具箱中向下滚动,找到标记为 AJAX 扩展的选项卡。 (如果没有此选项卡,因为使用的是较旧版本的 AJAX 或 Atlas 扩展,请参阅本白皮书稍后注册 AJAX 扩展工具箱项的演练,或者通过可从网站下载的 Windows Installer 安装当前版本) 。
  • (单击以查看全尺寸图像)

  • 已知问题:如果将 Visual Studio 2008 安装到已安装 Visual Studio 2005 的计算机上安装了 ASP.NET 2.0 AJAX 扩展,Visual Studio 2008 将导入 AJAX 扩展工具箱项。 可以通过检查组件的工具提示来确定是否为这种情况;他们应该说版本 3.5.0.0。 如果他们说版本 2.0.0.0,则你已导入旧工具箱项,并且需要使用 Visual Studio 中的“选择工具箱项”对话框手动导入它们。 无法通过设计器添加版本 2 控件。

  • <asp:Label> 标记开始之前,请创建一行空格,然后双击工具箱中的 UpdatePanel 控件。 请注意,页面顶部包含一个新 @Register 指令,指示应使用 asp: 前缀导入 System.Web.UI 命名空间中的控件。

  • 将结束 </asp:UpdatePanel> 标记拖过 Button 元素的末尾,以便该元素格式正确,并包装了“标签”和“按钮”控件。

  • 打开 <asp:UpdatePanel> 标记后,开始打开一个新标记。 请注意,IntelliSense 会提示你使用两个选项。 在这种情况下,请创建一个 <ContentTemplate> 标记。 请务必将此标记包装在“标签”和“按钮”周围,以便标记格式良好。

    (单击以查看全尺寸图像)

  • 元素中的任何 <form> 位置,通过双击 ScriptManager 工具箱中的项,包括 ScriptManager 控件。
  • <asp:ScriptManager>编辑标记,使其包含属性EnablePartialRendering= true
  • 列表 3:启用了部分呈现的 default.aspx 标记

    <%@ Page Language="C#" AutoEventWireup="true"
     CodeFile="Default.aspx.cs" Inherits="_Default" %>
    <%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
     PublicKeyToken=31bf3856ad364e35"
     Namespace="System.Web.UI" TagPrefix="asp" %>
    <!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>
     </head>
     <form id="form1" runat="server">
     <asp:ScriptManager EnablePartialRendering="true"
     ID="ScriptManager1" runat="server"></asp:ScriptManager>
     <asp:UpdatePanel ID="UpdatePanel1" runat="server">
     <ContentTemplate>
     <asp:Label ID="Label1" runat="server"
     Text="This is a label!"></asp:Label>
     <asp:Button ID="Button1" runat="server"
     Text="Click Me" OnClick="Button1_Click" />
     </ContentTemplate>
     </asp:UpdatePanel>
     </form>
     </body>
    </html>
    
  • 打开web.config文件。 请注意,Visual Studio 已自动向System.Web.Extensions.dll添加编译引用。

  • Visual Studio 2008 中的新增功能:ASP.NET 网站项目模板附带的web.config会自动包括对 ASP.NET AJAX 扩展的所有必要引用,并包括可以取消注释的配置信息的注释部分,以启用其他功能。 安装 ASP.NET 2.0 AJAX 扩展时,Visual Studio 2005 具有类似的模板。 但是,在 Visual Studio 2008 中,AJAX 扩展默认为选择退出 (,即默认引用它们,但可以将其删除为引用) 。

    (单击以查看全尺寸图像)

  • 按 F5 启动网站。 请注意,不支持部分呈现所需的源代码更改的方式 - 仅更改了标记。
  • 启动网站时,应会看到部分呈现现已启用,因为单击按钮时不会闪烁,也不会在页面滚动位置发生任何更改, (本示例不演示) 。 如果要在单击按钮后查看页面的呈现源,它将确认实际上尚未发生后退 - 原始标签文本仍然是源标记的一部分,并且标签已通过 JavaScript 更改。

    Visual Studio 2008 似乎没有为 ASP.NET AJAX-Enabled网站提供预定义模板。 但是,如果安装了 Visual Studio 2005 和 ASP.NET 2.0 AJAX 扩展,则 Visual Studio 2005 中提供了此类模板。 因此,配置网站并从AJAX-Enabled网站模板开始可能更容易,因为模板应包含完全配置的web.config文件, (支持所有 ASP.NET AJAX 扩展(包括 Web 服务访问和 JSON 序列化) - JavaScript 对象表示法) ,并在主Web Forms页中包含 UpdatePanel 和 ContentTemplate。 使用此默认页面启用部分呈现与重新访问本演练的步骤 10 一样简单,并将控件拖放到页面上。

    ScriptManager 控件

    ScriptManager 控件参考

    Markup-Enabled属性:

    ScriptManager 控件是 ASP.NET AJAX 扩展的基本核心。 它提供对脚本库的访问权限, (包括广泛的客户端脚本类型系统) ,支持部分呈现,并为其他 ASP.NET 服务 ((例如身份验证和分析)提供广泛的支持,但也提供对其他 Web 服务) 的广泛支持。 ScriptManager 控件还提供客户端脚本的全球化和本地化支持。

    提供替代脚本和补充脚本

    虽然 Microsoft ASP.NET 2.0 AJAX 扩展在调试版本和发布版本中包括整个脚本代码(作为嵌入在引用程序集中的资源),但开发人员可以自由地将 ScriptManager 重定向到自定义脚本文件,并注册其他必要的脚本。

    若要替代通常包含的脚本的默认绑定 (,例如支持 Sys.WebForms 命名空间和自定义键入系统) 的脚本,可以注册 ResolveScriptReference ScriptManager 类的事件。 调用此方法时,事件处理程序有机会更改有问题的脚本文件的路径;然后,脚本管理器会将脚本的不同或自定义副本发送到客户端。

    此外,可以通过编程方式或通过标记包含由类) 表示 ScriptReference 的脚本引用 (。 为此,可以编程方式修改 ScriptManager.Scripts 集合,或包括 <asp:ScriptReference> 标记下的 <Scripts> 标记,这是 ScriptManager 控件的第一级子级。

    UpdatePanels 的自定义错误处理

    虽然更新由 UpdatePanel 控件指定的触发器处理,但对错误处理和自定义错误消息的支持由页面的 ScriptManager 控件实例处理。 这是通过将事件公开给页面来完成的, AsyncPostBackError该页面随后可以提供自定义异常处理逻辑。

    通过使用 AsyncPostBackError 事件,可以指定 AsyncPostBackErrorMessage 属性,然后会导致在回调完成后引发警报框。

    还可以使用客户端自定义,而不是使用默认警报框;例如,你可能想要显示自定义 <div> 元素,而不是默认浏览器模式对话框。 在这种情况下,可以在客户端脚本中处理错误:

    列表 5:用于显示自定义错误的客户端脚本

    <script type= text/javascript >
     Sys.WebForms.PageRequestManager.getInstance().add_EndRequest(Request_End);
     function Request_End(sender, args)
     if (args.get_error() != undefined)
     var errorMessage =  ;
     if (args.get_response().get_statusCode() ==  200 )
     errorMessage = args.get_error().message;
     // the server wasn't the problem...
     errorMessage =  An unknown error occurred... ;
     // do something with the errorMessage here.
     // now make sure the system knows we handled the error.
     args.set_errorHandled(true);
     // -->
    </script>
    

    简单地说,上述脚本在异步请求完成时向客户端 AJAX 运行时注册回调。 然后,它会检查是否报告了错误,如果是,则处理错误的详细信息,最后指示运行时在自定义脚本中处理错误。

    全球化和本地化支持

    ScriptManager 控件为脚本字符串和用户界面组件的本地化提供广泛的支持;但是,该主题超出了本白皮书的范围。 有关详细信息,请参阅白皮书、ASP.NET AJAX 扩展中的全球化支持。

    UpdatePanel 控件

    UpdatePanel 控件参考

    Markup-Enabled属性:

    <asp:AsyncPostBackTrigger> 指定调用给定 UpdatePanel 的部分页面呈现的触发器。 这可能不是作为有问题的 UpdatePanel 后代的控件。 精细到事件名称。触发器>的<子级。 <asp:PostBackTrigger> 指定导致整个页面刷新的控件。 这可能不是作为有问题的 UpdatePanel 后代的控件。 精细到对象。 触发器>的<子级。

    UpdatePanel 控件是用于分隔将参与 AJAX 扩展的部分呈现功能的服务器端内容的控件。 UpdatePanel 控件的数量没有限制,这些控件可以位于页面上,并且可以嵌套这些控件。 每个 UpdatePanel 都是隔离的,因此每个更新窗格可以独立工作, (你可以同时运行两个 UpdatePanel,呈现页面的不同部分,独立于页面的回发) 。

    UpdatePanel 控件主要处理控件触发器 - 默认情况下,在 UpdatePanel 中创建回发的任何控件注册为 UpdatePanel ContentTemplate 的触发器。 这意味着 UpdatePanel 可以使用默认数据绑定控件 ((例如 GridView) ),以及用户控件,并且可以在脚本中对其进行编程。

    默认情况下,当触发部分页面呈现时,页面上的所有 UpdatePanel 控件都将刷新,无论是否为此类操作定义了触发器的 UpdatePanel 控件。 例如,如果一个 UpdatePanel 定义了一个按钮控件,并且单击该按钮控件,则该页上的所有 UpdatePanel 控件将默认刷新。 这是因为,默认情况下, UpdateMode UpdatePanel 的属性设置为 Always。 或者,可以将 UpdateMode 属性设置为 Conditional,这意味着仅当命中特定触发器时,UpdatePanel 才会刷新。

    自定义控件说明

    UpdatePanel 可以添加到任何用户控件或自定义控件;但是,包含这些控件的页面还必须包含一个 ScriptManager 控件,其属性 EnablePartialRendering 设置为 true

    使用 Web 自定义控件时,你可能会考虑的一种方法是重写类的CompositeControl受保护CreateChildControls()方法。 为此,如果确定页面支持部分呈现,则可以在控件的子级与外部世界之间注入 UpdatePanel;否则,只需将子控件分层到容器 Control 实例中。

    UpdatePanel 注意事项

    UpdatePanel 作为黑盒操作,在 JavaScript XMLHttpRequest 的上下文中包装 ASP.NET 回发。 但是,在行为和速度方面,都有重要的性能注意事项。 若要了解 UpdatePanel 的工作原理,以便最好地确定其使用是否合适,则应检查 AJAX 交换。 以下示例使用现有站点,并且具有 Firebug 扩展的 Mozilla Firefox (Firebug 捕获 XMLHttpRequest 数据) 。

    考虑一个表单,其中还有其他一个邮政编码文本框,该文本框应该在窗体或控件上填充城市和州域。 此表单最终收集成员身份信息,包括用户的姓名、地址和联系信息。 根据特定项目的要求,需要考虑许多设计注意事项。

    (单击以查看全尺寸图像)

    (单击以查看全尺寸图像)

    在此应用程序的原始迭代中,生成了一个控件,其中包含整个用户注册数据,包括邮政编码、城市和州。 整个控件已包装在 UpdatePanel 中,并丢弃到 Web 窗体中。 当用户输入邮政编码时,UpdatePanel 会通过指定触发器或使用 ChildrenAsTriggers 属性设置为 true) 来检测后端中相应的 TextChanged 事件 (事件。 AJAX 在 UpdatePanel 中发布所有字段,如 FireBug 捕获 (在右侧) 上看到关系图。

    如屏幕截图所示,UpdatePanel 中每个控件的值都 (传送,在本例中,它们都是空) ,以及 ViewState 字段。 全部被告知,发送超过 9kb 的数据时,实际上只需要 5 个字节的数据才能发出此特定请求。 响应更加膨胀:总共将 57kb 发送到客户端,只需更新文本字段和下拉字段即可。

    还可能需要了解 AJAX 如何更新演示文稿 ASP.NET。 UpdatePanel 更新请求的响应部分显示在左侧的 Firebug 控制台显示中;它是由客户端脚本分解的专门设计的管道分隔字符串,然后在页面上重新组合。 具体而言,ASP.NET AJAX 在表示 UpdatePanel 的客户端上设置 HTML 元素的内部 HTML 属性。 当浏览器重新生成 DOM 时,会稍有延迟,具体取决于需要处理的信息量。

    DOM 的重新生成会触发许多其他问题:

    (单击以查看全尺寸图像)

  • 如果焦点 HTML 元素位于 UpdatePanel 中,它将失去焦点。 因此,对于按下 Tab 键退出邮政编码文本框的用户,其下一个目标将是“城市”文本框。 但是,更新Panel 刷新显示后,窗体将不再具有焦点,按 Tab 将开始突出显示焦点元素 (,例如链接) 。
  • 如果正在使用任何类型的自定义客户端脚本来访问 DOM 元素,则部分回发后函数保留的引用可能会失效。
  • UpdatePanels 不打算成为所有解决方案。 相反,它们为某些情况提供了快速的解决方案,包括原型制作、小型控件更新,并为 ASP.NET 可能熟悉 .NET 对象模型但不太熟悉 DOM 的开发人员提供熟悉的界面。 有多种替代方法可能会导致性能更好,具体取决于应用程序方案:

  • 请考虑使用 PageMethods 和 JSON (JavaScript 对象表示法) 允许开发人员在页面上调用静态方法,就像正在调用 Web 服务调用一样。 由于方法是静态的,因此不需要状态;脚本调用方提供参数,结果以异步方式返回。
  • 如果需要在应用程序中的多个位置使用单个控件,请考虑使用 Web 服务和 JSON。 这再次需要很少的特殊工作,并异步工作。
  • 通过 Web 服务或页面方法合并功能也有缺点。 首先,ASP.NET 开发人员通常会将功能的小组件构建到用户控件 (.ascx 文件) 。 页方法不能托管在这些文件中;它们必须托管在实际的 .aspx 页类中。 同样,Web 服务必须托管在 .asmx 类中。 根据应用程序,此体系结构可能会违反单一责任原则,即单个组件的功能现在分布在两个或多个物理组件中,这些组件可能几乎没有或没有凝聚力关系。

    最后,如果应用程序要求使用 UpdatePanel,则以下准则应有助于进行故障排除和维护。

  • 嵌套 UpdatePanels 尽可能少,不仅在单元内,而且跨代码单元。 例如,在包装控件的页面上具有 UpdatePanel,而该控件还包含 UpdatePanel(包含包含 UpdatePanel 的另一个控件)是跨单元嵌套。 这有助于清除应刷新哪些元素,并防止对子 UpdatePanels 进行意外刷新。
  • ChildrenAsTriggers 属性设置为 false,并显式设置触发事件。 利用 <Triggers> 集合是处理事件的更清晰的方法,并可能会阻止意外行为,帮助维护任务并强制开发人员选择加入事件。
  • 使用尽可能小的单元来实现功能。 如对邮政编码服务的讨论中所述,只包装最少的时间,减少到服务器的时间、总处理和客户端-服务器交换的占用情况,从而提高性能。
  • UpdateProgress 控件

    UpdateProgress 控件参考

    Markup-Enabled属性:

    UpdateProgress 控件提供反馈度量值,以保持用户的兴趣,同时执行必要的工作以传输到服务器。 这可以帮助你的用户知道你正在执行某些操作,即使它可能不明显,尤其是因为大多数用户用于刷新页面并看到状态栏突出显示。

    请注意,UpdateProgress 控件可以在页面层次结构的任意位置显示。 但是,如果从子 UpdatePanel (启动部分回发,其中 UpdatePanel 嵌套在另一个 UpdatePanel) 中,触发子 UpdatePanel 的回发将导致为子 UpdatePanel 以及父 UpdatePanel 显示 UpdateProgress 模板。 但是,如果触发器是父 UpdatePanel 的直接子级,则只会显示与父级关联的 UpdateProgress 模板。

    Microsoft ASP.NET AJAX 扩展是一些复杂的产品,旨在帮助 Web 内容更易于访问,并为 Web 应用程序提供更丰富的用户体验。 作为 AJAX 扩展 ASP.NET 的一部分,部分页面呈现控件,包括 ScriptManager、UpdatePanel 和 UpdateProgress 控件是工具包中一些最可见的组件。

    ScriptManager 组件集成了客户端 JavaScript 的扩展预配,并使各种服务器端和客户端组件能够与最少的开发投资协同工作。

    UpdatePanel 控件是明显的魔术框 - UpdatePanel 中的标记可以具有服务器端 Codebehind,而不是触发页面刷新。 UpdatePanel 控件可以嵌套,并且可以依赖于其他 UpdatePanels 中的控件。 默认情况下,UpdatePanels 处理其后代控件调用的任何回发,尽管此功能可以声明方式或编程方式进行微调。

    使用 UpdatePanel 控件时,开发人员应了解可能出现的性能影响。 潜在的替代方法包括 Web 服务和页面方法,不过应考虑应用程序的设计。

    UpdateProgress 控件允许用户知道她或他不会被忽略,并且后台请求在页面不执行任何操作以响应用户输入时正在进行中。 它还包括中止部分呈现结果的功能。

    这些工具一起有助于创建丰富的无缝用户体验,使服务器对用户不那么明显,并且减少工作流中断。

    Scott Cate 自 1997 年以来一直在与 Microsoft Web 技术合作,是 myKB.com (www.myKB.com) 的总裁,他专门编写专注于知识库软件解决方案的基于 ASP.NET 的应用程序。 斯科特可以通过电子邮件联系,scott.cate@myKB.com也可以通过电子邮件联系他在 ScottCate.com 的博客