正文

Atlas 实现机制浅析2006-06-07 14:13:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/Csharpsky/15534.html

分享到:

1.3 局部重绘模式的服务器端响应

在第一小节中,我们曾提到 ScriptManager 在重载的 Web.UI.Control.OnInit 事件中,会根据页面请求中 delta = true 是否存在,判断当前页面是否处于局部重绘模式中,并接管 LoadComplete 时间来处理此模式。相应的 OnInit 事件还会在局部重绘模式中,主动接管 Page.Render 方法的逻辑来替换完整页面刷新。

1protectedoverridevoidOnInit(EventArgse)
2{
3//当不处于设计模式,且控件属于某个页面时
4if(!DesignMode&&(_page!=null))
5{
6//判断页面中是否只有一个ScriptManager实例,否则抛出异常
7
8//如果页面请求中delta属性为true则处于重绘模式
9if(_page.Request.Headers["delta"]=="true")
10{
11_inPartialRenderingMode=true;//处于重绘模式
12_page.TraceEnabled=false;//关闭trace支持
13
14//根据每个UpdatePanel的重绘状态,返回实际的重绘结果
15_page.LoadComplete+=newEventHandler(this.OnPageLoadComplete);
16}

17
18//完成前面提到的Altas.js和XML脚本的输出
19_page.PreRenderComplete+=newEventHandler(this.OnPagePreRenderComplete);
20}

21}

22
23privatevoidOnPagePreRenderComplete(objectsender,EventArgse)
24{
25//是否在局部重绘模式中
26if(_inPartialRenderingMode)
27{
28//接管Page的Render方法
29Page.SetRenderMethodDelegate(newRenderMethod(RenderPageCallback));
30return;
31}

32
33//
34}


在 OnPageLoadComplete 中,将遍历通过 RegisterUpdatePanel 注册到 ScriptManager 的所有 UpdatePanel,评估哪些区域是真正需要进行更新的 (UpdatePanel,评估哪些.RequiresUpdate = true),伪代码如下:

1privatevoidOnPageLoadComplete(objectsender,EventArgse)
2{
3for(UpdatePanelpanelin_allUpdatePanels)
4{
5if(panel是Page.Form的子控件&&panel.RequiresUpdate)
6{
7panel.SetPartialRenderingMode(true);
8_updatePanels.Add(panel1);
9}

10}

11}

而 RenderPageCallback 中,则将取代 Page.Render 的原本逻辑,根据整理出的 _updatePanels 列表中的区域进行重绘。返回的内容将是一个 XML 格式的文档,包括重绘的内容()、重绘的区域()以及相关 XML 脚本()等。实现的伪代码如下:

1privatevoidRenderPageCallback(HtmlTextWriterwriter,ControlpageControl)
2{
3Pagepage=(Page)pageControl;
4HttpResponseresponse=page1.Response;
5
6//关闭HTML缓存,设置返回文档类型为text/xml
7response.Cache.SetCacheability(HttpCacheability.NoCache);
8response.ContentType="text/xml";
9
10//输出HTML头内容
11writer.Write("");
12page.Header.RenderControl(writer);
13
14//输出Form成员的内容
15HtmlFormform=page.Form;
16form.SetRenderMethodDelegate(newRenderMethod(this.RenderFormCallback));
17form.RenderControl(writer);
18
19writer.Write("");
20
21//输出重绘UpdatePanel的ID列表
22writer.Write("");
23for(UpdatePanelpanelin_updatePanels)
24{
25//添加逗号分隔符
26
27writer.Write(updatePanels.ClientID);
28}

29writer.Write("");
30
31//输出XML脚本,如引用等
32writer.Write("");
33RenderXmlScript(writer);
34writer.Write("");
35writer.Write("");
36}


实际的针对控件的重绘逻辑,在 RenderFormCallback 中完成。此函数将针对 _updatePanels 中保存的需要进行重绘的区域,调用其 RenderControl 方法绘制整个子控件树。如果 Page.EnableEventValidation 选项打开,还会通过一个空 HtmlTextWriter 来模拟调用所有的控件输出,来模拟完整的事件引发流程。但其输出的内容被直接抛弃,避免冗余内容通过网络传输。完整的伪代码如下:

1privatevoidRenderFormCallback(HtmlTextWriterwriter,ControlcontainerControl)
2{
3for(UpdatePanelpanelin_updatePanels)
4{
5panel.RenderControl(writer);
6}

7
8if(Page.EnableEventValidation)
9{
10DummyHtmlTextWriterwriter=newDummyHtmlTextWriter();
11
12for(ControlcontrolincontainerControl.Controls)
13{
14control.RenderControl(writer);
15}

16}

17}

而在客户端浏览器中,依照上节中的分析,后台更新请求将通过 Web.Net.WebRequest 的封装,以 XMLHTTP 方式发送给页面;处理结果将由 request 对象上注册的 _onFormSubmitCompleted 事件进行解析。
_onFormSubmitCompleted 事件中,首先会对请求的返回状态进行检测,如果出错则进入错误模式并返回;如果返回正常,则先检查请求返回值是否是重定向命令,是则刷新窗口到新地址并返回;然后会根据前面提到的 deltaPanels 标签中 ID 列表,调用 _updatePanel 函数分别对每个区域进行更新;最后,会对隐藏的 input 域、页面标题、HTML 头中的 css 以及 XML 脚本等特殊标签进行处理。伪代码如下:

1_onFormSubmitCompleted=function(sender,eventArgs)
2{
3varisErrorMode=true;//是否处于错误模式
4varresponse=sender.get_response();
5vardelta;//实际返回的更新内容
6
7//请求成功则对返回内容进行解析
8if(response.get_statusCode()==200)
9{
10if(delta=response.get_xml())
11{
12//对IE浏览器来说,选择XPath作为解析语言
13if(Web.Application.get_type()==Web.ApplicationType.InternetExplorer)
14delta.setProperty('SelectionLanguage','XPath');
15
16//返回内容中如果有pageError节点则说明服务器端处理出现异常
17if(errorNode=delta.selectSingleNode("/delta/pageError"))
18isErrorMode=false;
19}

20}

21
22//如果发生错误则进入错误模式
23if(isErrorMode)
24{
25_enterErrorMode(errorNode?errorNode.attributes.getNamedItem('message').nodeValue:'Unknownerror');
26return;
27}

28
29//如果有页面重定向命令则重定向窗口
30if(redirectNode=delta.selectSingleNode("/delta/pageRedirect"))
31{
32window.location=redirectNode.attributes.getNamedItem('location').nodeValue
33return;
34}

35
36for(遍历delta.selectSingleNode("/delta/deltaPanels/text()")中每个节点)
37{
38_updatePanel(deltaPanelID,目标区域);
39}

40
41for(遍历delta.selectNodes('/delta/rendering//input[@type="hidden"]')中每个隐藏input域)
42{
43//向page.form中插入新的隐藏域
44}

45
46//如果有title节点则修改文档标题
47vartitle=delta.selectSingleNode('/delta/rendering//title/text()')
48document.title=title?title.nodeValue.trim()?'';
49
50//如果有style节点则更新css
51if(styleSheetMarkup=delta.selectSingleNode('/delta/rendering/head/style[position()=last()]'))
52_updateStyleSheet(styleSheetMarkup.text);
53
54//如果有脚本节点则更新脚本,否则调用_onFormSubmitCompletedCallback完成解析
55if(scripts=delta.selectNodes('/delta/rendering//script[@type="text/javascript"]'))
56_updateScripts(scripts);
57else
58_onFormSubmitCompletedCallback();
59}

这里对异常的处理,是 Altas M1 版本新增的功能。在前面所分析的 RenderPageCallback 方法中,通过一个 try...catch 将完整的局部重绘页面操作保护起来。如果有异常发生,则调用 OnPageError 事件进行实际处理,并最终通过 OnError 方法将异常信息返回给调用客户端。伪代码如下:

1privatevoidRenderPageCallback(HtmlTextWriterwriter,ControlpageControl)
2{
3//设置返回内容格式类型等
4
5writer.Write("");
6
7try
8{
9//局部重绘页面操作
10}

11catch(Exceptione)
12{
13OnPageError(e);
14}

15
16writer.Write("");
17}

18
19privatevoidOnPageError(Exceptionex)
20{
21PageErrorEventArgsargs=newPageErrorEventArgs(ex);
22OnPageError(args);
23ScriptManager.OnError(args.ErrorMessage,_page.Server,_page.Response);
24}

25
26privatestaticvoidOnError(stringerrorMessage,IHttpServerUtilityhttpServer,IHttpResponseresponse)
27{
28httpServer.ClearError();
29response.Clear();
30response.Cache.SetCacheability(HttpCacheability.NoCache);
31response.ContentType="text/xml";
32response.Write("");
33response.Write("""+HttpUtility.HtmlAttributeEncode(errorMessage)+"\"/>");
34response.Write("");
35}

而在 ScriptManager 中,可以通过 ErrorTemplate 标签定义异常信息的显式模板,类似

<atlas:ScriptManagerrunat="server">
<ErrorTemplate>
Therewasanerrorprocessingyouraction.
<br/>
<spanid="errorMessageLabel">span>
<hr/>
<buttontype="button"id="okButton">OKbutton>
ErrorTemplate>
atlas:ScriptManager>

而 ScriptManager.RenderErrorTemplate 方法会根据模板内容,生成名称为 __ErrorContainer 的 HTML 元素,并最终在客户端解析返回值的 _enterErrorMode 函数中进行更新。

而对重绘区域进行更新的 _updatePanel 函数,将根据 deltaPanels 中给出的 ID,定位到目标的更新区域 span 标签。并将其所有子控件进行析构 (dispose) 和删除 (removeChild),并用 rendering 中返回的内容替换之。

1_updatePanel=function(panelID,rendering)
2{
3varupdatePanelElement=document.getElementById(panelID);
4
5varelementsToDestroy=[];
6varchildCount=updatePanelElement.children.length;
7
8for(vari=0;i<childCount;i++)
9{
10elementsToDestroy.add(updatePanelElement.children[i]);
11}

12
13for(varj=0;j<elementsToDestroy.length;j++)
14{
15if(elementsToDestroy[j].control)
16elementsToDestroy[j].control.dispose();
17
18updatePanelElement.removeChild(elementsToDestroy[j]);
19}

20
21updatePanelElement.innerHTML=rendering;
22}


除了上述异常处理的流程外,Altas M1 还在 OnInit 方法中接管了局部重绘模式下的 IHttpContext.ApplicationInstance 对象的 PreSendRequestHeaders 和 Error 事件,分别用于处理页面重定向和全局异常的情况。具体实现机制与上述异常处理机制较为类似,这里就不一一分析了。

至此,一个完整的 Altas 异步请求和局部重绘模式的流程就基本分析完成了,后面有时间将继续就 WebService 支持、数据绑定等实现进行分析,而其原理基本上都是基于之前两节所分析的模式,只不过根据具体的应用有所变化。

阅读(2530) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册