ASP.NET Ajax 控制并不是一个独立的神秘组件,而是依靠 ScriptManager、UpdatePanel、UpdateProgress 和 Timer 等服务器控件,配合客户端脚本库实现无整页回发的交互体验。要真正用好它,首先要明白一个关键事实:所谓局部刷新,不是浏览器只更新某一块 DOM 那么简单,而是服务器仍然会执行完整的页面生命周期,只不过最后只把目标区域的 HTML 发送回前端进行替换。

一、先理清 ScriptManager 与 UpdatePanel 的职责边界
初学 ASP.NET Ajax 时,很多人会把 UpdatePanel 当成万能局部刷新容器,却忽略了它必须依赖 ScriptManager 才能工作。ScriptManager 是整个 ASP.NET Ajax 页面的总调度中心,负责向前端输出 Microsoft Ajax 客户端脚本库,并为后续的异步回发提供代理对象。一个页面中只能存在一个 ScriptManager 实例,如果因为使用母版页或用户控件而意外放置了多个,页面加载阶段就会抛出异常。因此,最稳妥的做法是在母版页中只声明一个 ScriptManager,内容页通过 ScriptManagerProxy 来补充配置。
下面这段代码展示的是一个最基础的局部刷新结构:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="TimeLabel" runat="server" Text="当前时间:" />
<asp:Button ID="RefreshButton" runat="server" Text="刷新时间" OnClick="RefreshButton_Click" />
</ContentTemplate>
</asp:UpdatePanel>
</form>
在这个结构中,UpdatePanel 内部的按钮默认会触发异步回发,服务器端仍然执行 Page_Load 和 RefreshButton_Click,但最终返回给浏览器的只有 UpdatePanel 区域内的 HTML。这里有一个非常容易被低估的事实:页面生命周期并没有缩短,数据库查询、复杂计算等服务器端开销一点都不会少。局部刷新真正节省的是整页 HTML 传输和浏览器重新渲染的成本,而不是服务器处理时间。如果按钮事件里有一段慢查询,改成 UpdatePanel 后响应时间也不会下降,只是用户看到页面不再闪一下而已。
因此,学习 ASP.NET Ajax 控制的第一步不是急着使用控件,而是要理解它的边界:ScriptManager 负责脚本与代理,UpdatePanel 负责划分局部区域,但真正决定性能的是服务器端逻辑和面板内外控件的依赖关系。
二、UpdatePanel 的操作要点:模式、触发器和等待提示
UpdatePanel 有两个核心属性直接决定刷新行为,一个是 UpdateMode,另一个是 ChildrenAsTriggers。UpdateMode 的默认值为 Always,意思是页面中任意一个异步回发都会导致这个面板重新渲染。这在面板数量较少时没有问题,可一旦页面中有多个 UpdatePanel,默认行为就会让一个按钮操作同时刷新所有面板,带来不必要的传输和重复渲染。将 UpdateMode 设为 Conditional 后,面板只会在自己的内部控件触发时刷新,或者由显式配置的 Triggers 触发。
下面是一个使用 Conditional 模式和外部触发器的例子:
<asp:UpdatePanel ID="PanelA" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false">
<ContentTemplate>
<asp:Label ID="TimeLabel" runat="server" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="RefreshButton" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
<asp:Button ID="RefreshButton" runat="server" Text="外部刷新" OnClick="RefreshButton_Click" />
这里 ChildrenAsTriggers="false" 表示面板内部的控件不再自动触发异步回发,只有当外部按钮 RefreshButton 的 Click 事件发生时,才会通过 AsyncPostBackTrigger 触发该面板刷新。这种写法适合把更新逻辑集中在一个提交按钮上,避免面板内部多个输入控件每次操作都产生请求。需要注意的是,AsyncPostBackTrigger 的 ControlID 必须是页面中真实存在的服务器控件 ID,如果写在用户控件内部,还需要配合控件的命名容器来正确解析。
异步请求期间如果没有明确的视觉反馈,用户很容易重复点击按钮,造成多次提交。UpdateProgress 控件就是为这个场景设计的。它会在关联的异步回发开始时显示一段提示内容,请求结束自动隐藏。可以设置 AssociatedUpdatePanelID 让它只针对某个面板生效,也可以通过 DisplayAfter="500" 让请求超过 500 毫秒后才显示,避免快速响应时出现一闪而过的加载动画。另一个经常被忽略的细节是:当服务器端发生未处理异常时,异步请求可能只返回错误状态却不显示任何信息,给排查带来困难。此时可以在 ScriptManager 上设置 AsyncPostBackErrorMessage,或者监听客户端事件 AsyncPostBackError,把异常信息展示给用户或记录到日志中。
三、常见疑问与误区排查:少走弯路的几个判断
第一个高频问题是局部刷新后 JavaScript 绑定失效。UpdatePanel 在异步回发后会用服务器返回的新 HTML 替换面板内部内容,原先通过 jQuery 直接绑定在按钮或容器上的事件会随着旧 DOM 被销毁而丢失。最简单的解决方式是使用事件委托,把事件绑定到面板外的稳定容器上。如果项目仍在使用 jQuery,可以用 on 方法绑定到 document 或外层容器。更适合 ASP.NET Ajax 环境的做法是订阅 Sys.WebForms.PageRequestManager 的 endRequest 事件,在每个异步请求完成后重新执行一次初始化逻辑。
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function () {
// 在这里重新绑定事件、初始化日期控件或执行其他客户端逻辑
bindEvents();
});
第二个常见疑问是多个 UpdatePanel 为什么会一起刷新。多数情况下是因为没有修改 UpdateMode,所有面板仍然保持着 Always 的默认状态。解决方法是把互不相关的面板都设置为 Conditional,并分别为它们配置自己的触发器。如果页面布局允许,也可以把一个大面板拆成多个小面板,只让发生变化的那块区域重新渲染。反过来,也要避免把整个页面都塞进一个 UpdatePanel,因为这样每次异步回发都在传输接近整页的 HTML,局部刷新的意义就大打折扣。
第三个误区是认为 UpdatePanel 可以包治性能问题。实际上它主要优化的是前端体验,对服务器端压力几乎没有降低,反而可能因为频繁的异步请求增加服务器负担。如果列表数据量很大,或者需要实时交互,更合适的方案可能是使用 Web API、Page Methods 配合前端框架,只传输 JSON 数据。此外,使用 Timer 控件做定时刷新时,要特别注意它在请求期间是否还在继续触发,必要时在 Tick 事件里先停止计时器,处理完成后再启动,防止请求堆积。对于普通 HTML 控件,只有加上 runat="server" 才能被服务器端操作,否则即使位于 UpdatePanel 内部,服务器也无法直接更新它的内容。
ASP.NET AjaxUpdatePanel异步刷新修改时间:2026-09-29 12:08:36