ASP.NET AJAX UpdatePanel 控件实现剖析

使用ASP.NET AJAX框架我们可以搭建快速响应、具有丰富的用户体验的AJAX Web应用程序,而该框架的UpdatePanel控件则提供了一种非常简单的方式来实现Web页面的局部更新,我们不需要在每次回发的时候都加载整个页面。

那这个控件是如何实现这种局部刷新的哪,透过其实现机制我们可以更清楚其优缺点,便于我们确定其使用场合。本文将重点阐述ASP.NET AJAX控件UpdatePanel的实现机制。

1. ASP.NET AJAX 简介 ASP.NET AJAX是微软在ASP.NET 2.0之上对AJAX技术的一个封装,为Web应用程序提供完整的AJAX解决方案。ASP.NET AJAX有两种编程模型:部分更新和远程服务。

部分更新使得用户可以用传统的ASP.NET 2.0应用程序的方式来搭建AJAX应用,具体就是使用UpdatePanel控件来实现无闪烁页面更新。而远程服务则是直接通过前端JavaScript来调用的服务器端服务,前段获取数据后,进行页面更新,这就要求服务器端代码必须分解为特定于应用程序的服务,这是与传统的ASP.NET应用程序完全不同的体系结构。

部分更新着重于对现有应用程序进行渐进式增强,帮助用户逐渐转换到纯粹的AJAX应用。本文主要对部分更新编程模型中核心控件UpdatePanel的实现进行剖析,讲述其背后的故事。

ASP.NET AJAX框架分为客户端以及服务器端两个部分,基于客户端的 Microsoft AJAX Library包含了对浏览器兼容性、网络访问以及客户端控件组件等支持, 而服务器端则包括了服务器控件,Web 服务等等。

见下图:

ASP.NET AJAX UpdatePanel 控件实现剖析

Microsoft Ajax Library就是ASP.NET AJAX的客户端脚本库,其中MicrosoftAjax.js包含了ASP.NET AJAX的核心内容,包括跨浏览器的支持、基于面向对象对JavaScript的扩展以及网络访问等等。MicrosoftAjaxWebForm.js文件则是完全服务于ASP.NET AJAX页面局部更新这样一个功能的,在该文件中定义了一个客户端对象PageRequestManager,该对象将会负责客户端异步回送的全过程。

2. ScriptManager 和 UpdatePanel ScriptManager和UpdatePanel是ASP.NET AJAX服务器端中最重要的两个控件,ScriptManager控件用来管理ASP.NET页面中的客户端脚本,生成及注册所需要的客户端脚本,通过UpdatePanel控件可以更新页面的指定部分而无需加载整个页面。

看个例子:

01 <form id="form1" runat="server">
02 <asp:ScriptManager ID="ScriptManager1" runat="server">
03 </asp:ScriptManager>
04 <div>
05 <asp:UpdatePanel ID="UpdatePanel1" runat="server">
06 <ContentTemplate>
07 <%= DateTime.Now %>
08 <br />
09 <asp:Button ID="Button1" runat="server" Text="Button" />
10 </ContentTemplate>
11 </asp:UpdatePanel>
12 </div>
13 </form>

构建如上代码所示的页面,在Runtime点击UpdatePanel中的Button控件,则不会引起整个页面刷新,只是用来显示当前时间的Label得到更新。

这是如何实现的哪?

3. ASP.NET AJAX部分呈现剖析

3.1 先从客户端讲起

看一下上面的示例代码在客户端的HTML代码, 这里只列出核心部分,其他全部隐去。

01 <script type="text/javascript">
02 //<![CDATA[
03 Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1'));
04 Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [], 90);
05 //]]>
06 </script>
07
08 <div id="UpdatePanel1">
09 7/25/2008 4:54:36 PM
10 <br />
11 <input type="submit" name="Button1" value="Button" id="Button1" />
12 </div>

看一下上面的两句JavaScript代码,第一句代码中的_initialize 方法是客户端PageRequestManager对象上的静态方法,它会创建一个 PageRequestManager 类的全局实例,并将其初始化。在这个初始化函数中,ageRequestManager对象注册了当前表单对象的submit事件,以及window对象的load和unload事件。

而第二句代码则是通过PageRequestManager的getInstance方法来检索其唯一实例, 得到该实例后调用_updateControls方法来注册UpdatePanel以及其Trigger控件。

我们可以从MicrosoftAjaxWebForm.js文件中得到_updateControls方法的声明:

1 function Sys$WebForms$PageRequestManager$_updateControls(updatePanelIDs, asyncPostBackControlIDs, postBackControlIDs, asyncPostBackTimeout) {}

由其中第一个参数代表了当前页面上所有的UpdatePanel控件的ID集合,如果该UpdatePanel的ChildrenAsTrigger为True的话,应在ID前添加字符't',否则添加字符'f';而第二个参数是所有引发异步回送的控件ID;第三个参数是所有引发同步回送的控件ID;第四个参数设定了异步回送的Timeout时间,单位为秒。于PageRequestManager对象注册了当前表单的submit时间,所以每当页面有提交动作的时候,PageRequestManager对象就会介入,看一下PageRequestManager对象页面提交处理函数_onFormSubmit(evt)。

如果需要执行一次异步回送的话,会中止原有的普通浏览器会回发,代之使用XMLHttpRequest进行AJAX回发。在封装这个请求的时候,当前页面的所有字段以及视图状态都会被打包在请求中,另外还设置了这次Request的HTTP头:request.get_headers()['X-MicrosoftAjax'] = 'Delta=true';

在服务器端将会根据这个HTTP头标记来判定是否为一次AJAX异步回发。

_onFormSubmit(evt)函数代码:

001 function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
002 var continueSubmit = true;
003 var isCrossPost = this._isCrossPost;
004 this._isCrossPost = false;
005 if (this._onsubmit) {
006 continueSubmit = this._onsubmit();
007 }
008 if (continueSubmit) {
009 for (var i = 0; i < this._onSubmitStatements.length; i++) {
010 if (!this._onSubmitStatements[i]()) {
011 continueSubmit = false;
012 break;
013 }
014 }
015 }
016 if (!continueSubmit) {
017 if (evt) {
018 evt.preventDefault();
019 }
020 return;
021 }
022 var form = this._form;
023 if (isCrossPost) {
024 return;
025 }
026 if (this._activeDefaultButton && !this._activeDefaultButtonClicked) {
027 this._onFormElementActive(this._activeDefaultButton, 0, 0);
028 }
029 if (!this._postBackSettings.async) {
030 return;
031 }
032 var formBody = new Sys.StringBuilder();
033 formBody.append(encodeURIComponent(this._scriptManagerID) + '=' + encodeURIComponent(this._postBackSettings.panelID) + '&');
034 var count = form.elements.length;
035 for (var i = 0; i < count; i++) {
036 var element = form.elements[i];
037 var name = element.name;
038 if (typeof(name) === "undefined" || (name === null) || (name.length === 0)) {
039 continue;
040 }
041 var tagName = element.tagName;
042 if (tagName === 'INPUT') {
043 var type = element.type;
044 if ((type === 'text') ||
045 (type === 'password') ||
046 (type === 'hidden') ||
047 (((type === 'checkbox') || (type === 'radio')) && element.checked)) {
048 formBody.append(encodeURIComponent(name));
049 formBody.append('=');
050 formBody.append(encodeURIComponent(element.value));
051 formBody.append('&');
052 }
053 }
054 else if (tagName === 'SELECT') {
055 var optionCount = element.options.length;
056 for (var j = 0; j < optionCount; j++) {
057 var option = element.options[j];
058 if (option.selected) {
059 formBody.append(encodeURIComponent(name));
060 formBody.append('=');
061 formBody.append(encodeURIComponent(option.value));
062 formBody.append('&');
063 }
064 }
065 }
066 else if (tagName === 'TEXTAREA') {
067 formBody.append(encodeURIComponent(name));
068 formBody.append('=');
069 formBody.append(encodeURIComponent(element.value));
070 formBody.append('&');
071 }
072 }
073 if (this._additionalInput) {
074 formBody.append(this._additionalInput);
075 this._additionalInput = null;
076 }
077
078 var request = new Sys.Net.WebRequest();
079 var action = form.action;
080 if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
081 var queryIndex = action.indexOf('?');
082 if (queryIndex !== -1) {
083 var path = action.substr(0, queryIndex);
084 if (path.indexOf("%") === -1) {
085 action = encodeURI(path) + action.substr(queryIndex);
086 }
087 }
088 else if (action.indexOf("%") === -1) {
089 action = encodeURI(action);
090 }
091 }
092 request.set_url(action);
093 request.get_headers()['X-MicrosoftAjax'] = 'Delta=true';
094 request.get_headers()['Cache-Control'] = 'no-cache';
095 request.set_timeout(this._asyncPostBackTimeout);
096 request.add_completed(Function.createDelegate(this, this._onFormSubmitCompleted));
097 request.set_body(formBody.toString());
098 var handler = this._get_eventHandlerList().getHandler("initializeRequest");
099 if (handler) {
100 var eventArgs = new Sys.WebForms.InitializeRequestEventArgs(request, this._postBackSettings.sourceElement);
101 handler(this, eventArgs);
102 continueSubmit = !eventArgs.get_cancel();
103 }
104 if (!continueSubmit) {
105 if (evt) {
106 evt.preventDefault();
107 }
108 return;
109 }
110 this._scrollPosition = this._getScrollPosition();
111 this.abortPostBack();
112 handler = this._get_eventHandlerList().getHandler("beginRequest");
113 if (handler) {
114 var eventArgs = new Sys.WebForms.BeginRequestEventArgs(request, this._postBackSettings.sourceElement);
115 handler(this, eventArgs);
116 }
117
118 if (this._originalDoCallback) {
119 this._cancelPendingCallbacks();
120 }
121 this._request = request;
122 request.invoke();
123 if (evt) {
124 evt.preventDefault();
125 }
126 }

我们可以发现AJAX回发所提交的数据量其实跟普通回发过程中提交的数据量是一样的,并且还附加了一些额外信息。

3.2 服务器端的处理

AJAX回发请求到达服务器之后,当前页面的生命周期跟普通回发引起的请求是一样的,页面的Init、Load和Render等等事件都会被触发,差别只是在于AJAX回发使用了不同的呈现画法。

AJAX回发引起的请求生命周期:

ASP.NET AJAX UpdatePanel 控件实现剖析

从上图我们可以看到,页面的生命周期与普通回发是一样的,同样页面上的控件也会经历相应的生命周期。

先了解一下ScriptManager控件在服务器端的处理:

- OnInit:在Init事件中,ScriptManager控件会注册页面的InitComplete, PreRenderComplete以及PreRender事件,另外还会根据本次请求的HTTP头来设定一个标记以确定本次回发是否为Ajax异步更新所引起的回发。

见下面的代码:

01 protected internal override void OnInit(EventArgs e)
02 {
03 base.OnInit(e);
04 if (this.EnableHistory)
05 {
06 this.RegisterAsyncPostBackControl(this);
07 }
08 if (!base.DesignMode)
09 {
10 IPage iPage = this.IPage;
11 if (GetCurrent(this.Page) != null)
12 {
13 throw new InvalidOperationException(AtlasWeb.ScriptManager_OnlyOneScriptManager);
14 }
15 iPage.Items[typeof(IScriptManager)] = this;
16 iPage.Items[typeof(ScriptManager)] = this;
17 iPage.InitComplete += new EventHandler(this.OnPageInitComplete);
18 iPage.PreRenderComplete += new EventHandler(this.OnPagePreRenderComplete);
19 if (iPage.IsPostBack)
20 {
21 this._isInAsyncPostBack = PageRequestManager.IsAsyncPostBackRequest(iPage.Request.Headers);
22 if (this.EnableHistory)
23 {
24 this._isNavigating = iPage.Request["__EVENTTARGET"] == this.UniqueID;
25 }
26 }
27 this.PageRequestManager.OnInit();
28 iPage.PreRender += new EventHandler(this.ScriptControlManager.OnPagePreRender);
29 }
30 }

- OnPagePreRenderComplete,在PagePreRenderComplete事件中,ScriptManager控件会注册脚本文件以及Services代理脚本,MicrosoftAjax.js和MicrosoftAjaxWebForm.js就是在这个阶段被注册到客户端的。

见下面的代码:

01 private void OnPagePreRenderComplete(object sender, EventArgs e)
02 {
03 if (!this.IsInAsyncPostBack)
04 {
05 if (this.SupportsPartialRendering)
06 {
07 this.IPage.ClientScript.GetPostBackEventReference(new PostBackOptions(this, null, null, false, false, false, false, true, null));
08 }
09 this.RegisterGlobalizationScriptBlock();
10 this.RegisterScripts();
11 this.RegisterServices();
12 if (this.EnableHistory)
13 {
14 JavaScriptSerializer serializer = JavaScriptSerializer.CreateInstance();
15 string[] strArray = new string[] { "/r/nSys.Application.setServerId(", serializer.Serialize(this.ClientID), ", ", serializer.Serialize(this.UniqueID), ");/r/n", ((this._initialState != null) && (this._initialState.Count != 0)) ? (" Sys.Application.setServerState('" + this.GetStateString() + "');/r/n") : "/r/n" };
16 string script = string.Concat(strArray);
17 RegisterStartupScript(this, typeof(ScriptManager), "HistoryStartup", script, true);
18 }
19 }
20 else
21 {
22 this.RegisterScripts();
23 if (this.EnableHistory)
24 {
25 if ((this._initialState != null) && (this._initialState.Count == 0))
26 {
27 this._initialState = null;
28 }
29 if (this._newPointCreated)
30 {
31 this.RegisterDataItem(this, "'" + this.GetStateString() + "'", true);
32 }
33 }
34 }
35 }

- OnPreRender,在PreRender事件中如果判定本次回发为AJAX回发,则会调用PageRequestManager对象的OnPreRender方法。而PageRequestManager对象则会调用Page对象的SetRenderMethodDelegate方法来代理Page的画法,PageRequestManager对象会真正负责本次AJAX回发最终的HTML代码。

见下面的代码:

01 public class ScriptManager : Control,
02 {
03 protected internal override void OnPreRender(EventArgs e)
04 {
05 base.OnPreRender(e);
06 if (this.IsInAsyncPostBack)
07 {
08 this.PageRequestManager.OnPreRender();
09 }
10 }
11 }
12
13 internal sealed class PageRequestManager
14 {
15 internal void OnPreRender()
16 {
17 this._owner.IPage.SetRenderMethodDelegate(new RenderMethod(this.RenderPageCallback));
18 }
19 }

PageRequestManager的RenderPageCallback方法最终处理了AJAX回发所需要的HTML代码,在这个方法中会遍历页面上所有涉及到的UpdatePanel控件,得到其更新后的HTML代码后,与隐藏字段还有一些额外信息一起打包,然后传递给客户端。

见下面的代码:

01 private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)
02 {
03 this.ProcessUpdatePanels();
04 IHttpResponse response = this._owner.IPage.Response;
05 response.ContentType = "text/plain";
06 response.Cache.SetNoServerCaching();
07 IHtmlForm form = this._owner.IPage.Form;
08 form.SetRenderMethodDelegate(new RenderMethod(this.RenderFormCallback));
09 this._updatePanelWriter = writer;
10 ParserStringWriter writer2 = new ParserStringWriter();
11 ParserHtmlTextWriter writer3 = new ParserHtmlTextWriter(writer2);
12 writer2.ParseWrites = true;
13 form.RenderControl(writer3);
14 writer2.ParseWrites = false;
15 foreach (KeyValuePair<string, string> pair in writer2.HiddenFields)
16 {
17 if (ControlUtil.IsBuiltInHiddenField(pair.Key))
18 {
19 EncodeString(writer, "hiddenField", pair.Key, pair.Value);
20 }
21 }
22 EncodeString(writer, "asyncPostBackControlIDs", string.Empty, this.GetAsyncPostBackControlIDs(false));
23 EncodeString(writer, "postBackControlIDs", string.Empty, this.GetPostBackControlIDs(false));
24 EncodeString(writer, "updatePanelIDs", string.Empty, this.GetAllUpdatePanelIDs());
25 EncodeString(writer, "childUpdatePanelIDs", string.Empty, this.GetChildUpdatePanelIDs());
26 EncodeString(writer, "panelsToRefreshIDs", string.Empty, this.GetRefreshingUpdatePanelIDs());
27 EncodeString(writer, "asyncPostBackTimeout", string.Empty, this._owner.AsyncPostBackTimeout.ToString(CultureInfo.InvariantCulture));
28 if (writer3.FormAction != null)
29 {
30 EncodeString(writer, "formAction", string.Empty, writer3.FormAction);
31 }
32 if (this._owner.IPage.Header != null)
33 {
34 string title = this._owner.IPage.Title;
35 if (!string.IsNullOrEmpty(title))
36 {
37 EncodeString(writer, "pageTitle", string.Empty, title);
38 }
39 }
40 this.RenderDataItems(writer);
41 this.ProcessScriptRegistration(writer);
42 this.ProcessFocus(writer);
43 }

3.3 客户端更新

当服务器端相应完毕后,客户端会得到响应信息,然后调用客户端对象PageRequestManager的_onFormSubmitCompleted方法来进行页面局部更新,最终会调用_updatePanel方法来更新UpdatePanel控件。

参见_onFormSubmitCompleted方法的代码:

001 function Sys$WebForms$PageRequestManager$_onFormSubmitCompleted(sender, eventArgs) {
002 this._processingRequest = true;
003 var delimitByLengthDelimiter = '|';
004 if (sender.get_timedOut()) {
005 this._endPostBack(this._createPageRequestManagerTimeoutError(), sender);
006 return;
007 }
008 if (sender.get_aborted()) {
009 this._endPostBack(null, sender);
010 return;
011 }
012 if (!this._request || sender.get_webRequest() !== this._request) {
013 return;
014 }
015 var errorMessage;
016 var delta = [];
017 if (sender.get_statusCode() !== 200) {
018 this._endPostBack(this._createPageRequestManagerServerError(sender.get_statusCode()), sender);
019 return;
020 }
021 var reply = sender.get_responseData();
022 var delimiterIndex, len, type, id, content;
023 var replyIndex = 0;
024 var parserErrorDetails = null;
025 while (replyIndex < reply.length) {
026 delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex);
027 if (delimiterIndex === -1) {
028 parserErrorDetails = this._findText(reply, replyIndex);
029 break;
030 }
031 len = parseInt(reply.substring(replyIndex, delimiterIndex), 10);
032 if ((len % 1) !== 0) {
033 parserErrorDetails = this._findText(reply, replyIndex);
034 break;
035 }
036 replyIndex = delimiterIndex + 1;
037 delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex);
038 if (delimiterIndex === -1) {
039 parserErrorDetails = this._findText(reply, replyIndex);
040 break;
041 }
042 type = reply.substring(replyIndex, delimiterIndex);
043 replyIndex = delimiterIndex + 1;
044 delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex);
045 if (delimiterIndex === -1) {
046 parserErrorDetails = this._findText(reply, replyIndex);
047 break;
048 }
049 id = reply.substring(replyIndex, delimiterIndex);
050 replyIndex = delimiterIndex + 1;
051 if ((replyIndex + len) >= reply.length) {
052 parserErrorDetails = this._findText(reply, reply.length);
053 break;
054 }
055 content = reply.substr(replyIndex, len);
056 replyIndex += len;
057 if (reply.charAt(replyIndex) !== delimitByLengthDelimiter) {
058 parserErrorDetails = this._findText(reply, replyIndex);
059 break;
060 }
061 replyIndex++;
062 Array.add(delta, {type: type, id: id, content: content});
063 }
064 if (parserErrorDetails) {
065 this._endPostBack(this._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_ParserErrorDetails, parserErrorDetails)), sender);
066 return;
067 }
068 var updatePanelNodes = [];
069 var hiddenFieldNodes = [];
070 var arrayDeclarationNodes = [];
071 var scriptBlockNodes = [];
072 var scriptStartupNodes = [];
073 var expandoNodes = [];
074 var onSubmitNodes = [];
075 var dataItemNodes = [];
076 var dataItemJsonNodes = [];
077 var scriptDisposeNodes = [];
078 var asyncPostBackControlIDsNode, postBackControlIDsNode,
079 updatePanelIDsNode, asyncPostBackTimeoutNode,
080 childUpdatePanelIDsNode, panelsToRefreshNode, formActionNode;
081 for (var i = 0; i < delta.length; i++) {
082 var deltaNode = delta[i];
083 switch (deltaNode.type) {
084 case "updatePanel":
085 Array.add(updatePanelNodes, deltaNode);
086 break;
087 case "hiddenField":
088 Array.add(hiddenFieldNodes, deltaNode);
089 break;
090 case "arrayDeclaration":
091 Array.add(arrayDeclarationNodes, deltaNode);
092 break;
093 case "scriptBlock":
094 Array.add(scriptBlockNodes, deltaNode);
095 break;
096 case "scriptStartupBlock":
097 Array.add(scriptStartupNodes, deltaNode);
098 break;
099 case "expando":
100 Array.add(expandoNodes, deltaNode);
101 break;
102 case "onSubmit":
103 Array.add(onSubmitNodes, deltaNode);
104 break;
105 case "asyncPostBackControlIDs":
106 asyncPostBackControlIDsNode = deltaNode;
107 break;
108 case "postBackControlIDs":
109 postBackControlIDsNode = deltaNode;
110 break;
111 case "updatePanelIDs":
112 updatePanelIDsNode = deltaNode;
113 break;
114 case "asyncPostBackTimeout":
115 asyncPostBackTimeoutNode = deltaNode;
116 break;
117 case "childUpdatePanelIDs":
118 childUpdatePanelIDsNode = deltaNode;
119 break;
120 case "panelsToRefreshIDs":
121 panelsToRefreshNode = deltaNode;
122 break;
123 case "formAction":
124 formActionNode = deltaNode;
125 break;
126 case "dataItem":
127 Array.add(dataItemNodes, deltaNode);
128 break;
129 case "dataItemJson":
130 Array.add(dataItemJsonNodes, deltaNode);
131 break;
132 case "scriptDispose":
133 Array.add(scriptDisposeNodes, deltaNode);
134 break;
135 case "pageRedirect":
136 if (Sys.Browser.agent === Sys.Browser.InternetExplorer) {
137 var anchor = document.createElement("a");
138 anchor.style.display = 'none';
139 anchor.attachEvent("onclick", cancelBubble);
140 anchor.href = deltaNode.content;
141 document.body.appendChild(anchor);
142 anchor.click();
143 anchor.detachEvent("onclick", cancelBubble);
144 document.body.removeChild(anchor);
145
146 function cancelBubble(e) {
147 e.cancelBubble = true;
148 }
149 }
150 else {
151 window.location.href = deltaNode.content;
152 }
153 return;
154 case "error":
155 this._endPostBack(this._createPageRequestManagerServerError(Number.parseInvariant(deltaNode.id), deltaNode.content), sender);
156 return;
157 case "pageTitle":
158 document.title = deltaNode.content;
159 break;
160 case "focus":
161 this._controlIDToFocus = deltaNode.content;
162 break;
163 default:
164 this._endPostBack(this._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_UnknownToken, deltaNode.type)), sender);
165 return;
166 }
167 }
168 var i;
169 if (asyncPostBackControlIDsNode && postBackControlIDsNode &&
170 updatePanelIDsNode && panelsToRefreshNode &&
171 asyncPostBackTimeoutNode && childUpdatePanelIDsNode) {
172 this._oldUpdatePanelIDs = this._updatePanelIDs;
173 var childUpdatePanelIDsString = childUpdatePanelIDsNode.content;
174 this._childUpdatePanelIDs = childUpdatePanelIDsString.length ? childUpdatePanelIDsString.split(',') : [];
175 var asyncPostBackControlIDsArray = this._splitNodeIntoArray(asyncPostBackControlIDsNode);
176 var postBackControlIDsArray = this._splitNodeIntoArray(postBackControlIDsNode);
177 var updatePanelIDsArray = this._splitNodeIntoArray(updatePanelIDsNode);
178 this._panelsToRefreshIDs = this._splitNodeIntoArray(panelsToRefreshNode);
179 for (i = 0; i < this._panelsToRefreshIDs.length; i++) {
180 var panelClientID = this._uniqueIDToClientID(this._panelsToRefreshIDs[i]);
181 if (!document.getElementById(panelClientID)) {
182 this._endPostBack(Error.invalidOperation(String.format(Sys.WebForms.Res.PRM_MissingPanel, panelClientID)), sender);
183 return;
184 }
185 }
186 var asyncPostBackTimeout = asyncPostBackTimeoutNode.content;
187 this._updateControls(updatePanelIDsArray, asyncPostBackControlIDsArray, postBackControlIDsArray, asyncPostBackTimeout);
188 }
189 this._dataItems = {};
190 for (i = 0; i < dataItemNodes.length; i++) {
191 var dataItemNode = dataItemNodes[i];
192 this._dataItems[dataItemNode.id] = dataItemNode.content;
193 }
194 for (i = 0; i < dataItemJsonNodes.length; i++) {
195 var dataItemJsonNode = dataItemJsonNodes[i];
196 this._dataItems[dataItemJsonNode.id] = Sys.Serialization.JavaScriptSerializer.deserialize(dataItemJsonNode.content);
197 }
198 var handler = this._get_eventHandlerList().getHandler("pageLoading");
199 if (handler) {
200 handler(this, this._getPageLoadingEventArgs());
201 }
202 if (formActionNode) {
203 this._form.action = formActionNode.content;
204 }
205
206
207 Sys._ScriptLoader.readLoadedScripts();
208 Sys.Application.beginCreateComponents();
209 var scriptLoader = Sys._ScriptLoader.getInstance();
210 this._queueScripts(scriptLoader, scriptBlockNodes, true, false);
211
212 this._updateContext = {
213 response: sender,
214 updatePanelNodes: updatePanelNodes,
215 scriptBlockNodes: scriptBlockNodes,
216 scriptDisposeNodes: scriptDisposeNodes,
217 hiddenFieldNodes: hiddenFieldNodes,
218 arrayDeclarationNodes: arrayDeclarationNodes,
219 expandoNodes: expandoNodes,
220 scriptStartupNodes: scriptStartupNodes,
221 onSubmitNodes: onSubmitNodes
222 };
223 scriptLoader.loadScripts(0,
224 Function.createDelegate(this, this._scriptIncludesLoadComplete),
225 Function.createDelegate(this, this._scriptIncludesLoadFailed),
226 null);
227 }

4.结语

使用UpdatePanel是给已经存在的ASP.NET应用程序添加AJAX体验的最快捷方式,对于应用程序的架构也不会有影响,我们可以使用它来逐步的提高应用程序的用户体验。但是其性能与纯粹的AJAX方式相比较,还是比较差的。

你可能感兴趣的:(asp.net)