PageRequestManager的RenderPageCallback方法最终处理了AJAX回发所需要的HTML代码,在这个方法中会遍历页面上所有涉及到的UpdatePanel控件,得到其更新后的HTML代码后,与隐藏字段还有一些额外信息一起打包,然后传递给客户端。
见下面的代码:
private void RenderPageCallback(HtmlTextWriter writer, Control pageControl) { this.ProcessUpdatePanels(); IHttpResponse response = this._owner.IPage.Response; response.ContentType = "text/plain"; response.Cache.SetNoServerCaching(); IHtmlForm form = this._owner.IPage.Form; form.SetRenderMethodDelegate(new RenderMethod(this.RenderFormCallback)); this._updatePanelWriter = writer; ParserStringWriter writer2 = new ParserStringWriter(); ParserHtmlTextWriter writer3 = new ParserHtmlTextWriter(writer2); writer2.ParseWrites = true; form.RenderControl(writer3); writer2.ParseWrites = false; foreach (KeyValuePair<string, string> pair in writer2.HiddenFields) { if (ControlUtil.IsBuiltInHiddenField(pair.Key)) { EncodeString(writer, "hiddenField", pair.Key, pair.Value); } } EncodeString(writer, "asyncPostBackControlIDs", string.Empty, this.GetAsyncPostBackControlIDs(false)); EncodeString(writer, "postBackControlIDs", string.Empty, this.GetPostBackControlIDs(false)); EncodeString(writer, "updatePanelIDs", string.Empty, this.GetAllUpdatePanelIDs()); EncodeString(writer, "childUpdatePanelIDs", string.Empty, this.GetChildUpdatePanelIDs()); EncodeString(writer, "panelsToRefreshIDs", string.Empty, this.GetRefreshingUpdatePanelIDs()); EncodeString(writer, "asyncPostBackTimeout", string.Empty, this._owner.AsyncPostBackTimeout.ToString(CultureInfo.InvariantCulture)); if (writer3.FormAction != null) { EncodeString(writer, "formAction", string.Empty, writer3.FormAction); } if (this._owner.IPage.Header != null) { string title = this._owner.IPage.Title; if (!string.IsNullOrEmpty(title)) { EncodeString(writer, "pageTitle", string.Empty, title); } } this.RenderDataItems(writer); this.ProcessScriptRegistration(writer); this.ProcessFocus(writer); }
3.3 客户端更新
当服务器端相应完毕后,客户端会得到响应信息,然后调用客户端对象PageRequestManager的_onFormSubmitCompleted方法来进行页面局部更新,最终会调用_updatePanel方法来更新UpdatePanel控件。
参见_onFormSubmitCompleted方法的代码:
function Sys$WebForms$PageRequestManager$_onFormSubmitCompleted(sender, eventArgs) { this._processingRequest = true; var delimitByLengthDelimiter = '|'; if (sender.get_timedOut()) { this._endPostBack(this._createPageRequestManagerTimeoutError(), sender); return; } if (sender.get_aborted()) { this._endPostBack(null, sender); return; } if (!this._request || sender.get_webRequest() !== this._request) { return; } var errorMessage; var delta = []; if (sender.get_statusCode() !== 200) { this._endPostBack(this._createPageRequestManagerServerError(sender.get_statusCode()), sender); return; } var reply = sender.get_responseData(); var delimiterIndex, len, type, id, content; var replyIndex = 0; var parserErrorDetails = null; while (replyIndex < reply.length) { delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex); if (delimiterIndex === -1) { parserErrorDetails = this._findText(reply, replyIndex); break; } len = parseInt(reply.substring(replyIndex, delimiterIndex), 10); if ((len % 1) !== 0) { parserErrorDetails = this._findText(reply, replyIndex); break; } replyIndex = delimiterIndex + 1; delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex); if (delimiterIndex === -1) { parserErrorDetails = this._findText(reply, replyIndex); break; } type = reply.substring(replyIndex, delimiterIndex); replyIndex = delimiterIndex + 1; delimiterIndex = reply.indexOf(delimitByLengthDelimiter, replyIndex); if (delimiterIndex === -1) { parserErrorDetails = this._findText(reply, replyIndex); break; } id = reply.substring(replyIndex, delimiterIndex); replyIndex = delimiterIndex + 1; if ((replyIndex + len) >= reply.length) { parserErrorDetails = this._findText(reply, reply.length); break; } content = reply.substr(replyIndex, len); replyIndex += len; if (reply.charAt(replyIndex) !== delimitByLengthDelimiter) { parserErrorDetails = this._findText(reply, replyIndex); break; } replyIndex++; Array.add(delta, {type: type, id: id, content: content}); } if (parserErrorDetails) { this._endPostBack(this._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_ParserErrorDetails, parserErrorDetails)), sender); return; } var updatePanelNodes = []; var hiddenFieldNodes = []; var arrayDeclarationNodes = []; var scriptBlockNodes = []; var scriptStartupNodes = []; var expandoNodes = []; var onSubmitNodes = []; var dataItemNodes = []; var dataItemJsonNodes = []; var scriptDisposeNodes = []; var asyncPostBackControlIDsNode, postBackControlIDsNode, updatePanelIDsNode, asyncPostBackTimeoutNode, childUpdatePanelIDsNode, panelsToRefreshNode, formActionNode; for (var i = 0; i < delta.length; i++) { var deltaNode = delta[i]; switch (deltaNode.type) { case "updatePanel": Array.add(updatePanelNodes, deltaNode); break; case "hiddenField": Array.add(hiddenFieldNodes, deltaNode); break; case "arrayDeclaration": Array.add(arrayDeclarationNodes, deltaNode); break; case "scriptBlock": Array.add(scriptBlockNodes, deltaNode); break; case "scriptStartupBlock": Array.add(scriptStartupNodes, deltaNode); break; case "expando": Array.add(expandoNodes, deltaNode); break; case "onSubmit": Array.add(onSubmitNodes, deltaNode); break; case "asyncPostBackControlIDs": asyncPostBackControlIDsNode = deltaNode; break; case "postBackControlIDs": postBackControlIDsNode = deltaNode; break; case "updatePanelIDs": updatePanelIDsNode = deltaNode; break; case "asyncPostBackTimeout": asyncPostBackTimeoutNode = deltaNode; break; case "childUpdatePanelIDs": childUpdatePanelIDsNode = deltaNode; break; case "panelsToRefreshIDs": panelsToRefreshNode = deltaNode; break; case "formAction": formActionNode = deltaNode; break; case "dataItem": Array.add(dataItemNodes, deltaNode); break; case "dataItemJson": Array.add(dataItemJsonNodes, deltaNode); break; case "scriptDispose": Array.add(scriptDisposeNodes, deltaNode); break; case "pageRedirect": if (Sys.Browser.agent === Sys.Browser.InternetExplorer) { var anchor = document.createElement("a"); anchor.style.display = 'none'; anchor.attachEvent("onclick", cancelBubble); anchor.href = deltaNode.content; document.body.appendChild(anchor); anchor.click(); anchor.detachEvent("onclick", cancelBubble); document.body.removeChild(anchor); function cancelBubble(e) { e.cancelBubble = true; } } else { window.location.href = deltaNode.content; } return; case "error": this._endPostBack(this._createPageRequestManagerServerError(Number.parseInvariant(deltaNode.id), deltaNode.content), sender); return; case "pageTitle": document.title = deltaNode.content; break; case "focus": this._controlIDToFocus = deltaNode.content; break; default: this._endPostBack(this._createPageRequestManagerParserError(String.format(Sys.WebForms.Res.PRM_UnknownToken, deltaNode.type)), sender); return; } } var i; if (asyncPostBackControlIDsNode && postBackControlIDsNode && updatePanelIDsNode && panelsToRefreshNode && asyncPostBackTimeoutNode && childUpdatePanelIDsNode) { this._oldUpdatePanelIDs = this._updatePanelIDs; var childUpdatePanelIDsString = childUpdatePanelIDsNode.content; this._childUpdatePanelIDs = childUpdatePanelIDsString.length ? childUpdatePanelIDsString.split(',') : []; var asyncPostBackControlIDsArray = this._splitNodeIntoArray(asyncPostBackControlIDsNode); var postBackControlIDsArray = this._splitNodeIntoArray(postBackControlIDsNode); var updatePanelIDsArray = this._splitNodeIntoArray(updatePanelIDsNode); this._panelsToRefreshIDs = this._splitNodeIntoArray(panelsToRefreshNode); for (i = 0; i < this._panelsToRefreshIDs.length; i++) { var panelClientID = this._uniqueIDToClientID(this._panelsToRefreshIDs[i]); if (!document.getElementById(panelClientID)) { this._endPostBack(Error.invalidOperation(String.format(Sys.WebForms.Res.PRM_MissingPanel, panelClientID)), sender); return; } } var asyncPostBackTimeout = asyncPostBackTimeoutNode.content; this._updateControls(updatePanelIDsArray, asyncPostBackControlIDsArray, postBackControlIDsArray, asyncPostBackTimeout); } this._dataItems = {}; for (i = 0; i < dataItemNodes.length; i++) { var dataItemNode = dataItemNodes[i]; this._dataItems[dataItemNode.id] = dataItemNode.content; } for (i = 0; i < dataItemJsonNodes.length; i++) { var dataItemJsonNode = dataItemJsonNodes[i]; this._dataItems[dataItemJsonNode.id] = Sys.Serialization.JavaScriptSerializer.deserialize(dataItemJsonNode.content); } var handler = this._get_eventHandlerList().getHandler("pageLoading"); if (handler) { handler(this, this._getPageLoadingEventArgs()); } if (formActionNode) { this._form.action = formActionNode.content; } Sys._ScriptLoader.readLoadedScripts(); Sys.Application.beginCreateComponents(); var scriptLoader = Sys._ScriptLoader.getInstance(); this._queueScripts(scriptLoader, scriptBlockNodes, true, false); this._updateContext = { response: sender, updatePanelNodes: updatePanelNodes, scriptBlockNodes: scriptBlockNodes, scriptDisposeNodes: scriptDisposeNodes, hiddenFieldNodes: hiddenFieldNodes, arrayDeclarationNodes: arrayDeclarationNodes, expandoNodes: expandoNodes, scriptStartupNodes: scriptStartupNodes, onSubmitNodes: onSubmitNodes }; scriptLoader.loadScripts(0, Function.createDelegate(this, this._scriptIncludesLoadComplete), Function.createDelegate(this, this._scriptIncludesLoadFailed), null); }
4.结语
使用UpdatePanel是给已经存在的ASP.NET应用程序添加AJAX体验的最快捷方式,对于应用程序的架构也不会有影响,我们可以使用它来逐步的提高应用程序的用户体验。但是其性能与纯粹的AJAX方式相比较,还是比较差的。