了解 ASP.NET AJAX UpdatePanel 触发器

作者 :Scott Cate

在 Visual Studio 中的标记编辑器中工作时,你可能会注意到 IntelliSense) (UpdatePanel 控件有两个子元素。 其中一个元素是 Triggers 元素,它指定页面上 (控件或用户控件,如果使用一个) 将触发元素所在的 UpdatePanel 控件的部分呈现。

简介

Microsoft 的 ASP.NET 技术带来了面向对象的事件驱动的编程模型,并将其与编译代码的优势结合在一起。 但是,其服务器端处理模型具有该技术固有的几个缺点,其中许多缺点可以通过 Microsoft ASP.NET 3.5 AJAX 扩展中包含的新功能来解决。 这些扩展支持许多新的丰富客户端功能,包括无需完整页面刷新的部分页面呈现、通过客户端脚本访问 Web 服务的功能 (包括 ASP.NET 分析 API) ,以及旨在镜像 ASP.NET 服务器端控件集中出现的许多控制方案的广泛客户端 API。

本白皮书介绍 ASP.NET AJAX UpdatePanel 组件的 XML 触发器功能。 XML 触发器提供对可能导致特定 UpdatePanel 控件部分呈现的组件的精细控制。

本白皮书基于 .NET Framework 3.5 和 Visual Studio 2008 的 Beta 2 版本。 ASP.NET AJAX 扩展(以前是面向 ASP.NET 2.0 的加载项程序集)现在已集成到 .NET Framework 基类库中。 本白皮书还假定你将使用 Visual Studio 2008,而不是 Visual Web Developer Express,并将根据 Visual Studio (用户界面提供演练,尽管无论开发环境) 如何,代码列表都将完全兼容。

触发器

默认情况下,给定 UpdatePanel 的触发器会自动包含调用回发的任何子控件,包括 (例如,) 属性设置为 true 的 TextBox 控件AutoPostBack。 但是,还可以使用标记以声明方式包含触发器;此操作在 <triggers> UpdatePanel 控件声明的 部分中完成。 尽管可以通过集合属性访问 Triggers 触发器,但建议在运行时注册任何部分呈现触发器 (例如,如果在设计时无法使用控件,) 在 RegisterAsyncPostBackControl(Control) 事件中使用 Page_Load 页面的 ScriptManager 对象的 方法。 请记住,Pages 是无状态的,因此应在每次创建这些控件时重新注册这些控件。

还可以 (禁用自动子触发器包含,以便创建回发的子控件不会通过将 属性设置为 ChildrenAsTriggersfalse 自动触发部分呈现) 。 这样,你可以以最大的灵活性分配哪些特定控件可以调用页面呈现(建议),以便开发人员选择响应事件,而不是处理可能出现的任何事件。

请注意,当嵌套 UpdatePanel 控件时,当 UpdateMode 设置为 Conditional 时,如果触发了子 UpdatePanel,但父级未触发,则只有子 UpdatePanel 将刷新。 但是,如果刷新父 UpdatePanel,则子 UpdatePanel 也将刷新。

Triggers <> 元素

在 Visual Studio 中的标记编辑器中工作时,你可能会注意到 IntelliSense) (控件有两个 UpdatePanel 子元素。 最常看到的元素是 <ContentTemplate> 元素,它实质上封装了更新面板将保留的内容 (我们要为其启用部分呈现) 的内容。 另一个元素是 <Triggers> 元素,该元素指定页面上的控件 (或用户控件,如果使用一个) 将触发 Triggers> 元素所在的 UpdatePanel 控件<的部分呈现。

元素 <Triggers> 可以包含两个子节点中的任意数字: <asp:AsyncPostBackTrigger><asp:PostBackTrigger>。 它们都接受和 这两个属性, ControlIDEventName并且可以指定当前封装单元中的任何控件 (例如,如果 UpdatePanel 控件驻留在 Web 用户控件中,则不应尝试引用用户控件将驻留) 页面上的控件。

元素 <asp:AsyncPostBackTrigger> 特别有用,因为它可以面向作为封装单元中 任何 UpdatePanel 控件的子级存在的 Control 中的任何事件,而不仅仅是此触发器作为子级触发器的 UpdatePanel。 因此,可以创建任何控件来触发部分页面更新。

同样, <asp:PostBackTrigger> 元素可用于触发部分页面呈现,但需要完整的往返服务器。 当控件通常触发部分页面呈现 (时,此触发器元素还可用于强制呈现整页,例如,当 updatePanel 控件的 元素) <ContentTemplate>中存在控件Button时。 同样,PostBackTrigger 元素可以指定作为当前封装单元中任何 UpdatePanel 控件的子级的任何控件。

<Triggers> 元素参考

标记后代:

标记 说明
<asp:AsyncPostBackTrigger> 指定一个控件和事件,该控件和事件将导致包含此触发器引用的 UpdatePanel 的部分页面更新。
<asp:PostBackTrigger> 指定将导致整页更新 (整页刷新) 的控件和事件。 当控件以其他方式触发部分呈现时,此标记可用于强制完全刷新。

演练:跨 UpdatePanel 触发器

  1. 创建一个新的 ASP.NET 页面,其中 ScriptManager 对象设置为启用部分呈现。 向此页面添加两个 UpdatePanel - 在第一个页面,包括 Label1 ) (一个 Label 控件和两个 Button 控件 ( Button1 和 Button2 ) 。 Button1 应显示“单击以更新两者”,Button2 应显示“单击以更新”或沿这些行的内容。 在第二个 UpdatePanel 中,仅包括 Label 控件 ( Label2 ) ,但将其 ForeColor 属性设置为默认值以外的其他属性以区分它。
  2. 将两个 UpdatePanel 标记的 UpdateMode 属性设置为 Conditional

列表 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>
   <body>
      <form id="form1" runat="server">
         <asp:ScriptManager EnablePartialRendering="true"
            ID="ScriptManager1" runat="server"></asp:ScriptManager>
         <div>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server"
               UpdateMode="Conditional">
               <ContentTemplate>
                  <asp:Label ID="Label1" runat="server" />
                  <br />
                  <asp:Button ID="Button1" runat="server"
                     Text="Update Both Panels" OnClick="Button1_Click" />
                  <asp:Button ID="Button2" runat="server"
                     Text="Update This Panel" OnClick="Button2_Click" />
               </ContentTemplate>
            </asp:UpdatePanel>
            <asp:UpdatePanel ID="UpdatePanel2" runat="server"
               UpdateMode="Conditional">
               <ContentTemplate>
                  <asp:Label ID="Label2" runat="server" ForeColor="red" />
               </ContentTemplate>
               <Triggers>
                  <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
               </Triggers>
            </asp:UpdatePanel>
         </div>
      </form>
   </body>
</html>

  1. 在 Button1 的 Click 事件处理程序中,将 Label1.Text 和 Label2.Text 设置为与时间相关的 (,例如 DateTime.Now.ToLongTimeString () ) 。 对于 Button2 的 Click 事件处理程序,仅将 Label1.Text 设置为时间相关值。

列表 2:codebehind (剪裁了 default.aspx.cs 中的) :

public partial class _Default : System.Web.UI.Page
{
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToLongTimeString();
        Label2.Text = DateTime.Now.ToLongTimeString();
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToLongTimeString();
    }
}
  1. 按 F5 生成并运行项目。 请注意,单击“更新两个面板”时,两个标签会更改文本;但是,单击“更新此面板”时,只会更新 Label1。

显示第一个按钮的屏幕截图,其中指出“更新两个面板”,第二个按钮表示“更新此面板”。

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

幕后

利用我们刚刚构造的示例,我们可以了解 AJAX ASP.NET 执行的操作以及 UpdatePanel 跨面板触发器的工作原理。 为此,我们将使用生成的页面源 HTML,以及名为 FireBug 的 Mozilla Firefox 扩展 - 使用它,我们可以轻松检查 AJAX 回发。 我们还将使用 Lutz Roeder 提供的 .NET 反射器工具。 这两种工具均可在线免费获取,可通过 Internet 搜索找到。

对页面源代码的检查几乎没有什么不同之处:UpdatePanel 控件呈现为 <div> 容器,我们可以看到 提供的 <asp:ScriptManager>脚本资源包括 。 还有一些对 PageRequestManager 的特定于 AJAX 的新调用,这些调用是 AJAX 客户端脚本库的内部调用。 最后,我们看到两个 UpdatePanel 容器 - 一个包含呈现 <input> 按钮,两个 <asp:Label> 控件呈现为 <span> 容器。 (如果在 FireBug 中检查 DOM 树,你会注意到标签变暗,以指示它们未) 生成可见内容。

单击“更新此面板”按钮,并注意顶部 UpdatePanel 将使用当前服务器时间进行更新。 在 FireBug 中,选择“控制台”选项卡,以便可以检查请求。 首先检查 POST 请求参数:

显示“Firebug”对话框的屏幕截图,其中选择了“控制台”。

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

请注意,UpdatePanel 已向服务器端 AJAX 代码指明了通过控件的 ScriptManager1 参数 Button1 触发的 UpdatePanel1 控件树。 现在,单击“更新两个面板”按钮。 然后,检查响应,我们看到字符串中设置了一系列以管道分隔的变量;具体而言,我们看到顶部的 UpdatePanel UpdatePanel1已将其全部 HTML 发送到浏览器。 AJAX 客户端脚本库通过 .innerHTML 属性将 UpdatePanel 的原始 HTML 内容替换为新内容,因此服务器将更改后的内容作为 HTML 从服务器发送。

现在,单击“更新两个面板”按钮并检查服务器的结果。 结果非常相似 - 两个 UpdatePanel 都从服务器接收新的 HTML。 与上一个回调一样,会发送其他页面状态。

正如我们所看到的,由于没有使用任何特殊代码来执行 AJAX 回发,因此 AJAX 客户端脚本库能够截获表单回发,而无需任何其他代码。 服务器控件自动利用 JavaScript,以便它们不会自动提交表单 - ASP.NET 自动注入代码以进行表单验证和状态,这主要通过自动脚本资源包含、PostBackOptions 类和 ClientScriptManager 类来实现。

例如,考虑 CheckBox 控件;检查 .NET 反射器中的类反汇编。 为此,请确保 System.Web 程序集处于打开状态,并导航到 System.Web.UI.WebControls.CheckBox 类并打开 RenderInputTag 方法。 查找检查属性的条件 AutoPostBack

显示以“单击等于”开头的代码的屏幕截图。

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

当通过 AutoPostBack 属性) true 对控件 (启用 CheckBox 自动回发时,生成的 <input> 标记将在其 onclick 属性中使用 ASP.NET 事件处理脚本呈现。 然后,对表单提交的截获允许 ASP.NET AJAX 以非侵入方式注入页面,从而帮助避免使用可能不精确字符串替换可能发生的任何潜在中断性变更。 此外,这允许 任何 自定义 ASP.NET 控件利用 ASP.NET AJAX 的强大功能,而无需任何其他代码来支持它在 UpdatePanel 容器中的使用。

该功能 <triggers> 对应于在对 _updateControls (的 PageRequestManager 调用中初始化的值,请注意,ASP.NET AJAX 客户端脚本库使用以下约定,即以下划线开头的方法、事件和字段名称标记为内部,并且不应在库本身之外使用) 。 有了它,我们可以观察哪些控件会导致 AJAX 回发。

例如,让我们向页面添加另外两个控件,将一个控件完全保留在 UpdatePanels 之外,将一个控件保留在 UpdatePanel 中。 我们将在 UpdatePanel 的上方添加一个 CheckBox 控件,并删除一个 DropDownList,其中包含列表中定义的多种颜色。 下面是新标记:

列表 3:新标记

<%@ 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 id="Head1" runat="server">
 <title>Untitled Page</title>
 </head>
 <body>
 <form id="form1" runat="server">
 <asp:ScriptManager EnablePartialRendering="true"
 ID="ScriptManager1" runat="server"></asp:ScriptManager>
 <div>
 <asp:UpdatePanel ID="UpdatePanel1" runat="server"
 UpdateMode="Conditional">
 <ContentTemplate>
 <asp:Label ID="Label1" runat="server" /><br />
 <asp:Button ID="Button1" runat="server"
 Text="Update Both Panels" OnClick="Button1_Click" />
 <asp:Button ID="Button2" runat="server"
 Text="Update This Panel" OnClick="Button2_Click" />
 <asp:CheckBox ID="cbDate" runat="server"
 Text="Include Date" AutoPostBack="false"
 OnCheckedChanged="cbDate_CheckedChanged" />
 </ContentTemplate>
 </asp:UpdatePanel>
 <asp:UpdatePanel ID="UpdatePanel2" runat="server"
 UpdateMode="Conditional">
 <ContentTemplate>
 <asp:Label ID="Label2" runat="server"
 ForeColor="red" />
 </ContentTemplate>
 <Triggers>
 <asp:AsyncPostBackTrigger ControlID="Button1" 
 EventName="Click" />
 <asp:AsyncPostBackTrigger ControlID="ddlColor" 
 EventName="SelectedIndexChanged" />
 </Triggers>
 </asp:UpdatePanel>
 <asp:DropDownList ID="ddlColor" runat="server"
 AutoPostBack="true"
 OnSelectedIndexChanged="ddlColor_SelectedIndexChanged">
 <asp:ListItem Selected="true" Value="Red" />
 <asp:ListItem Value="Blue" />
 <asp:ListItem Value="Green" />
 </asp:DropDownList>
 </div>
 </form>
 </body>
</html>

下面是新的代码隐藏:

列表 4:代码隐藏

public partial class _Default : System.Web.UI.Page
{
    protected void Button1_Click(object sender, EventArgs e)
    {
        if (cbDate.Checked)
        {
            Label1.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
            Label2.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
        }
        else
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
            Label2.Text = DateTime.Now.ToLongTimeString();
        }
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        if (cbDate.Checked)
        {
            Label1.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss");
        }
        else
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
        }
    }
    protected void cbDate_CheckedChanged(object sender, EventArgs e)
    {
        cbDate.Font.Bold = cbDate.Checked;
    }
    protected void ddlColor_SelectedIndexChanged(object sender, EventArgs e)
    {
        Color c = Color.FromName(ddlColor.SelectedValue);
        Label2.ForeColor = c;
    }
}

此页面背后的理念是,下拉列表选择三种颜色之一以显示第二个标签,检查框确定它是否为粗体,以及标签是否显示日期和时间。 检查框不应导致 AJAX 更新,但下拉列表应该如此,即使它不位于 UpdatePanel 中。

显示名为“无标题页面”的 Web 浏览器的屏幕截图,并在显示“更新两个面板”的按钮下方选择了蓝色的下拉列表。

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

如上面的屏幕截图所示,要单击的最新按钮是右按钮“更新此面板”,该按钮更新了顶部时间,与底部时间无关。 两次单击之间也关闭了日期,因为日期在底部标签中可见。 最后一个值得关注的是底部标签的颜色:它比标签的文本更新得最近,这表明控件状态很重要,用户希望通过 AJAX 回发来保留它。 但是,时间未更新。 当控件在服务器上重新呈现时,通过 ASP.NET 运行时解释的页面__VIEWSTATE字段的持久性自动重新填充时间。 ASP.NET AJAX 服务器代码无法识别控件更改状态的方法;它只是从视图状态重新填充,然后运行适当的事件。

但是,应指出的是,如果我在 Page_Load 事件内初始化了时间,时间就会正确递增。 因此,开发人员应警惕在适当的事件处理程序期间运行适当的代码,并避免在控件事件处理程序合适时使用Page_Load。

总结

ASP.NET AJAX Extensions UpdatePanel 控件用途广泛,可以使用多种方法来识别应导致更新它的控件事件。 它支持由其子控件自动更新,但也可以响应页面上其他位置的控件事件。

为了降低服务器处理负载的可能性,建议 ChildrenAsTriggers 将 UpdatePanel 的 属性设置为 false,并且选择加入事件,而不是默认包含事件。 这还可以防止任何不需要的事件造成可能不需要的效果,包括验证和对输入字段的更改。 这些类型的 bug 可能难以隔离,因为页面以透明方式向用户更新,因此原因可能并不明显。

通过检查 ASP.NET AJAX 表单后拦截模型的内部工作原理,我们能够确定它利用 ASP.NET 已提供的框架。 这样做时,它与使用相同框架设计的控件保持了最大的兼容性,并且对为页面编写的任何其他 JavaScript 都进行了最少的入侵。

个人简介

Rob Paveza 是 Terralever (www.terralever.com) 的高级 .NET 应用程序开发人员,该公司是亚利桑那州坦佩的一家领先的交互式营销公司。 可在 联系 robpaveza@gmail.com他,他的博客位于 http://geekswithblogs.net/robp/

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