使用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方式相比较,还是比较差的。
分享到:
相关推荐
5.3.3 利用ASP.NET AJAX实现内容集成 125 5.3.4 桥技术 131 5.4 使用ASP.NET应用服务 137 5.4.1 启用ASP.NET应用服务 137 5.4.2 验证服务 138 5.4.3 个性化 140 5.4.4 角色:Orcas预览 143 5.4.5 消息板应用...
本书通过大量实例、深入的描述以及代码分析,全面涵盖了ASP.NET AJAX服务器端和客户端框架。书中的所有代码都通过了ASP.NET 2.0和ASP.NET 3.5的测试。通过本书,您将学习到这些框架之间是如何进行协同以满足AJAx...
16.2 ASP.NET 3.5AJAX 控件 16.2.1 脚本管理控件(ScriptManger) 16.2.2 脚本管理控件(ScriptMangerProxy) 16.2.3 时间控件(Timer) 16.2.4 更新区域控件(UpdatePanel) 16.2.5 更新进度控件(UpdateProgress)...
16.2 ASP.NET 3.5AJAX控件 16.2.1 脚本管理控件(ScriptManger) 16.2.2 脚本管理控件(ScriptMangerProxy) 16.2.3 时间控件(Timer) 16.2.4 更新区域控件(UpdatePanel) 16.2.5 更新进度控件(UpdateProgress) ...
16.2 ASP.NET 3.5AJAX控件 16.2.1 脚本管理控件(ScriptManger) 16.2.2 脚本管理控件(ScriptMangerProxy) 16.2.3 时间控件(Timer) 16.2.4 更新区域控件(UpdatePanel) 16.2.5 更新进度控件(UpdateProgress) ...
16.2 ASP.NET 3.5AJAX控件 16.2.1 脚本管理控件(ScriptManger) 16.2.2 脚本管理控件(ScriptMangerProxy) 16.2.3 时间控件(Timer) 16.2.4 更新区域控件(UpdatePanel) 16.2.5 更新进度控件(UpdateProgress) ...
16.2 ASP.NET 3.5AJAX控件 16.2.1 脚本管理控件(ScriptManger) 16.2.2 脚本管理控件(ScriptMangerProxy) 16.2.3 时间控件(Timer) 16.2.4 更新区域控件(UpdatePanel) 16.2.5 更新进度控件(UpdateProgress) ...
此外,《ASP.NET 4高级程序设计(第4版)》专门提供了两章的内容来教你如何用Ajax 技术制作快速响应的页面,以及如何使用微软的ASP.NETAJAX平台。另外,还专门介绍了ASP.NET4 新增的功能,如MVC 和动态数据等。 ...
此外,《ASP.NET 4高级程序设计(第4版)》专门提供了两章的内容来教你如何用Ajax 技术制作快速响应的页面,以及如何使用微软的ASP.NETAJAX平台。另外,还专门介绍了ASP.NET4 新增的功能,如MVC 和动态数据等。 ...
再比如ASP.Net内置的AJAX解决方案UpdatePanel只在部分要求不高的内网项目中才被使用,因此我们在讲解UpdatePanel的使用和原理之外,把更多的时间放在讲解企业中用的最多的JQuery AJAX解决方案上。 6、B/S系统项目(7...
16.2 ASP.NET 3.5AJAX控件 16.2.1 脚本管理控件(ScriptManger) 16.2.2 脚本管理控件(ScriptMangerProxy) 16.2.3 时间控件(Timer) 16.2.4 更新区域控件(UpdatePanel) 16.2.5 更新进度控件(UpdateProgress) ...
4.6.6 在asp.net中引用名称空间 241 4.7 创建自己的名称空间 241 4.8 my关键字 244 4.8.1 my.application名称空间 244 4.8.2 my.computer名称空间 248 4.8.3 my.forms名称空间 251 4.8.4 my.resources...