使用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 服务等等。
见下图:
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" > |
05 |
< asp:UpdatePanel ID = "UpdatePanel1" runat = "server" > |
09 |
< asp:Button ID = "Button1" runat = "server" Text = "Button" /> |
构建如上代码所示的页面,在Runtime点击UpdatePanel中的Button控件,则不会引起整个页面刷新,只是用来显示当前时间的Label得到更新。
这是如何实现的哪?
3. ASP.NET AJAX部分呈现剖析
3.1 先从客户端讲起
看一下上面的示例代码在客户端的HTML代码, 这里只列出核心部分,其他全部隐去。
01 |
< script type = "text/javascript" > |
03 |
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', document.getElementById('form1')); |
04 |
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tUpdatePanel1'], [], [], 90); |
08 |
< div id = "UpdatePanel1" > |
11 |
< input type = "submit" name = "Button1" value = "Button" id = "Button1" /> |
看一下上面的两句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(); |
008 |
if (continueSubmit) { |
009 |
for (var i = 0; i < this ._onSubmitStatements.length; i++) { |
010 |
if (! this ._onSubmitStatements[i]()) { |
011 |
continueSubmit = false ; |
016 |
if (!continueSubmit) { |
018 |
evt.preventDefault(); |
022 |
var form = this ._form; |
026 |
if ( this ._activeDefaultButton && ! this ._activeDefaultButtonClicked) { |
027 |
this ._onFormElementActive( this ._activeDefaultButton, 0, 0); |
029 |
if (! this ._postBackSettings.async) { |
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)) { |
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( '&' ); |
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( '&' ); |
066 |
else if (tagName === 'TEXTAREA' ) { |
067 |
formBody.append(encodeURIComponent(name)); |
068 |
formBody.append( '=' ); |
069 |
formBody.append(encodeURIComponent(element.value)); |
070 |
formBody.append( '&' ); |
073 |
if ( this ._additionalInput) { |
074 |
formBody.append( this ._additionalInput); |
075 |
this ._additionalInput = null ; |
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); |
088 |
else if (action.indexOf( "%" ) === -1) { |
089 |
action = encodeURI(action); |
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" ); |
100 |
var eventArgs = new Sys.WebForms.InitializeRequestEventArgs(request, this ._postBackSettings.sourceElement); |
101 |
handler( this , eventArgs); |
102 |
continueSubmit = !eventArgs.get_cancel(); |
104 |
if (!continueSubmit) { |
106 |
evt.preventDefault(); |
110 |
this ._scrollPosition = this ._getScrollPosition(); |
111 |
this .abortPostBack(); |
112 |
handler = this ._get_eventHandlerList().getHandler( "beginRequest" ); |
114 |
var eventArgs = new Sys.WebForms.BeginRequestEventArgs(request, this ._postBackSettings.sourceElement); |
115 |
handler( this , eventArgs); |
118 |
if ( this ._originalDoCallback) { |
119 |
this ._cancelPendingCallbacks(); |
121 |
this ._request = request; |
124 |
evt.preventDefault(); |
我们可以发现AJAX回发所提交的数据量其实跟普通回发过程中提交的数据量是一样的,并且还附加了一些额外信息。
3.2 服务器端的处理
AJAX回发请求到达服务器之后,当前页面的生命周期跟普通回发引起的请求是一样的,页面的Init、Load和Render等等事件都会被触发,差别只是在于AJAX回发使用了不同的呈现画法。
AJAX回发引起的请求生命周期:
从上图我们可以看到,页面的生命周期与普通回发是一样的,同样页面上的控件也会经历相应的生命周期。
先了解一下ScriptManager控件在服务器端的处理:
- OnInit:在Init事件中,ScriptManager控件会注册页面的InitComplete, PreRenderComplete以及PreRender事件,另外还会根据本次请求的HTTP头来设定一个标记以确定本次回发是否为Ajax异步更新所引起的回发。
见下面的代码:
01 |
protected internal override void OnInit(EventArgs e) |
04 |
if ( this .EnableHistory) |
06 |
this .RegisterAsyncPostBackControl( this ); |
10 |
IPage iPage = this .IPage; |
11 |
if (GetCurrent( this .Page) != null ) |
13 |
throw new InvalidOperationException(AtlasWeb.ScriptManager_OnlyOneScriptManager); |
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); |
21 |
this ._isInAsyncPostBack = PageRequestManager.IsAsyncPostBackRequest(iPage.Request.Headers); |
22 |
if ( this .EnableHistory) |
24 |
this ._isNavigating = iPage.Request[ "__EVENTTARGET" ] == this .UniqueID; |
27 |
this .PageRequestManager.OnInit(); |
28 |
iPage.PreRender += new EventHandler( this .ScriptControlManager.OnPagePreRender); |
- OnPagePreRenderComplete,在PagePreRenderComplete事件中,ScriptManager控件会注册脚本文件以及Services代理脚本,MicrosoftAjax.js和MicrosoftAjaxWebForm.js就是在这个阶段被注册到客户端的。
见下面的代码:
01 |
private void OnPagePreRenderComplete( object sender, EventArgs e) |
03 |
if (! this .IsInAsyncPostBack) |
05 |
if ( this .SupportsPartialRendering) |
07 |
this .IPage.ClientScript.GetPostBackEventReference( new PostBackOptions( this , null , null , false , false , false , false , true , null )); |
09 |
this .RegisterGlobalizationScriptBlock(); |
10 |
this .RegisterScripts(); |
11 |
this .RegisterServices(); |
12 |
if ( this .EnableHistory) |
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 ); |
22 |
this .RegisterScripts(); |
23 |
if ( this .EnableHistory) |
25 |
if (( this ._initialState != null ) && ( this ._initialState.Count == 0)) |
27 |
this ._initialState = null ; |
29 |
if ( this ._newPointCreated) |
31 |
this .RegisterDataItem( this , "'" + this .GetStateString() + "'" , true ); |
- OnPreRender,在PreRender事件中如果判定本次回发为AJAX回发,则会调用PageRequestManager对象的OnPreRender方法。而PageRequestManager对象则会调用Page对象的SetRenderMethodDelegate方法来代理Page的画法,PageRequestManager对象会真正负责本次AJAX回发最终的HTML代码。
见下面的代码:
01 |
public class ScriptManager : Control, |
03 |
protected internal override void OnPreRender(EventArgs e) |
06 |
if ( this .IsInAsyncPostBack) |
08 |
this .PageRequestManager.OnPreRender(); |
13 |
internal sealed class PageRequestManager |
15 |
internal void OnPreRender() |
17 |
this ._owner.IPage.SetRenderMethodDelegate( new RenderMethod( this .RenderPageCallback)); |
PageRequestManager的RenderPageCallback方法最终处理了AJAX回发所需要的HTML代码,在这个方法中会遍历页面上所有涉及到的UpdatePanel控件,得到其更新后的HTML代码后,与隐藏字段还有一些额外信息一起打包,然后传递给客户端。
见下面的代码:
01 |
private void RenderPageCallback(HtmlTextWriter writer, Control pageControl) |
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) |
17 |
if (ControlUtil.IsBuiltInHiddenField(pair.Key)) |
19 |
EncodeString(writer, "hiddenField" , pair.Key, pair.Value); |
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 ) |
30 |
EncodeString(writer, "formAction" , string .Empty, writer3.FormAction); |
32 |
if ( this ._owner.IPage.Header != null ) |
34 |
string title = this ._owner.IPage.Title; |
35 |
if (! string .IsNullOrEmpty(title)) |
37 |
EncodeString(writer, "pageTitle" , string .Empty, title); |
40 |
this .RenderDataItems(writer); |
41 |
this .ProcessScriptRegistration(writer); |
42 |
this .ProcessFocus(writer); |
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); |
008 |
if (sender.get_aborted()) { |
009 |
this ._endPostBack( null , sender); |
012 |
if (! this ._request || sender.get_webRequest() !== this ._request) { |
017 |
if (sender.get_statusCode() !== 200) { |
018 |
this ._endPostBack( this ._createPageRequestManagerServerError(sender.get_statusCode()), sender); |
021 |
var reply = sender.get_responseData(); |
022 |
var delimiterIndex, len, type, id, content; |
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); |
031 |
len = parseInt(reply.substring(replyIndex, delimiterIndex), 10); |
032 |
if ((len % 1) !== 0) { |
033 |
parserErrorDetails = this ._findText(reply, replyIndex); |
036 |
replyIndex = delimiterIndex + 1; |
037 |
delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex); |
038 |
if (delimiterIndex === -1) { |
039 |
parserErrorDetails = this ._findText(reply, replyIndex); |
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); |
049 |
id = reply.substring(replyIndex, delimiterIndex); |
050 |
replyIndex = delimiterIndex + 1; |
051 |
if ((replyIndex + len) >= reply.length) { |
052 |
parserErrorDetails = this ._findText(reply, reply.length); |
055 |
content = reply.substr(replyIndex, len); |
057 |
if (reply.charAt(replyIndex) !== delimitByLengthDelimiter) { |
058 |
parserErrorDetails = this ._findText(reply, replyIndex); |
062 |
Array.add(delta, {type: type, id: id, content: content}); |
064 |
if (parserErrorDetails) { |
065 |
this ._endPostBack( this ._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_ParserErrorDetails, parserErrorDetails)), sender); |
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) { |
085 |
Array.add(updatePanelNodes, deltaNode); |
088 |
Array.add(hiddenFieldNodes, deltaNode); |
090 |
case "arrayDeclaration" : |
091 |
Array.add(arrayDeclarationNodes, deltaNode); |
094 |
Array.add(scriptBlockNodes, deltaNode); |
096 |
case "scriptStartupBlock" : |
097 |
Array.add(scriptStartupNodes, deltaNode); |
100 |
Array.add(expandoNodes, deltaNode); |
103 |
Array.add(onSubmitNodes, deltaNode); |
105 |
case "asyncPostBackControlIDs" : |
106 |
asyncPostBackControlIDsNode = deltaNode; |
108 |
case "postBackControlIDs" : |
109 |
postBackControlIDsNode = deltaNode; |
111 |
case "updatePanelIDs" : |
112 |
updatePanelIDsNode = deltaNode; |
114 |
case "asyncPostBackTimeout" : |
115 |
asyncPostBackTimeoutNode = deltaNode; |
117 |
case "childUpdatePanelIDs" : |
118 |
childUpdatePanelIDsNode = deltaNode; |
120 |
case "panelsToRefreshIDs" : |
121 |
panelsToRefreshNode = deltaNode; |
124 |
formActionNode = deltaNode; |
127 |
Array.add(dataItemNodes, deltaNode); |
130 |
Array.add(dataItemJsonNodes, deltaNode); |
132 |
case "scriptDispose" : |
133 |
Array.add(scriptDisposeNodes, deltaNode); |
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); |
143 |
anchor.detachEvent( "onclick" , cancelBubble); |
144 |
document.body.removeChild(anchor); |
146 |
function cancelBubble(e) { |
147 |
e.cancelBubble = true ; |
151 |
window.location.href = deltaNode.content; |
155 |
this ._endPostBack( this ._createPageRequestManagerServerError(Number.parseInvariant(deltaNode.id), deltaNode.content), sender); |
158 |
document.title = deltaNode.content; |
161 |
this ._controlIDToFocus = deltaNode.content; |
164 |
this ._endPostBack( this ._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_UnknownToken, deltaNode.type)), sender); |
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); |
186 |
var asyncPostBackTimeout = asyncPostBackTimeoutNode.content; |
187 |
this ._updateControls(updatePanelIDsArray, asyncPostBackControlIDsArray, postBackControlIDsArray, asyncPostBackTimeout); |
189 |
this ._dataItems = {}; |
190 |
for (i = 0; i < dataItemNodes.length; i++) { |
191 |
var dataItemNode = dataItemNodes[i]; |
192 |
this ._dataItems[dataItemNode.id] = dataItemNode.content; |
194 |
for (i = 0; i < dataItemJsonNodes.length; i++) { |
195 |
var dataItemJsonNode = dataItemJsonNodes[i]; |
196 |
this ._dataItems[dataItemJsonNode.id] = Sys.Serialization.JavaScriptSerializer.deserialize(dataItemJsonNode.content); |
198 |
var handler = this ._get_eventHandlerList().getHandler( "pageLoading" ); |
200 |
handler( this , this ._getPageLoadingEventArgs()); |
202 |
if (formActionNode) { |
203 |
this ._form.action = formActionNode.content; |
207 |
Sys._ScriptLoader.readLoadedScripts(); |
208 |
Sys.Application.beginCreateComponents(); |
209 |
var scriptLoader = Sys._ScriptLoader.getInstance(); |
210 |
this ._queueScripts(scriptLoader, scriptBlockNodes, true , false ); |
212 |
this ._updateContext = { |
214 |
updatePanelNodes: updatePanelNodes, |
215 |
scriptBlockNodes: scriptBlockNodes, |
216 |
scriptDisposeNodes: scriptDisposeNodes, |
217 |
hiddenFieldNodes: hiddenFieldNodes, |
218 |
arrayDeclarationNodes: arrayDeclarationNodes, |
219 |
expandoNodes: expandoNodes, |
220 |
scriptStartupNodes: scriptStartupNodes, |
221 |
onSubmitNodes: onSubmitNodes |
223 |
scriptLoader.loadScripts(0, |
224 |
Function.createDelegate( this , this ._scriptIncludesLoadComplete), |
225 |
Function.createDelegate( this , this ._scriptIncludesLoadFailed), |
4.结语
使用UpdatePanel是给已经存在的ASP.NET应用程序添加AJAX体验的最快捷方式,对于应用程序的架构也不会有影响,我们可以使用它来逐步的提高应用程序的用户体验。但是其性能与纯粹的AJAX方式相比较,还是比较差的。