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方式相比较,还是比较差的。


