Asp.net Ajax 1.0 異步回調時,服務器端Render原理首冼回顧一下
Page頁的生命週期
PreInit
Init
InitComplete
LoadState
ProcessPostData
PreLoad
Load
LoadComplete
PreRender
PrepareCallback //如果有回調
PreRenderComplete
SaveState
SaveStateComplete
Render
不論是Asp.net請求,還是Ajax請求,都要執行上面的頁生命週期,
在Page頁最後會調用Page.RenderControl()呈現Page所有子控件
流程如下
Page.RenderControl
Control.RenderControl()
Control.RenderControl(writer,this.Adapter)
Control.RenderControlInternal()
Control.Render() //此時Page.Render()將其重寫
Control.RenderChildren()
Control.RenderControlInternal()
現在關鍵所在
Control.RenderControlInternal()實現如下
如果不是Asp.net Ajax處理,肯定會執行foreach依次對所有的子控件進行遍歷
internal void RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
{
//正常情況如果不設置RareFields,進行默認的流程依次對所有子控件進行處理,
//如果設置RareFields,意思就是以自定義的方式改寫RenderChildren
if ((this.RareFields != null) && (this.RareFields.RenderMethod != null)) //當您SetRenderMethodDelegate時,RareFieldsEnsured就有值啦
{
writer.BeginRender();
//調用RenderMethod委託,Asp.net Ajax1.0在使用Control.SetRenderMethodDelegate時,對應的回調函數就是在此時處理的
this.RareFields.RenderMethod(writer, this);
writer.EndRender();
}
else if (children != null)
{
//如果不設置這個RareFieldsEnsured,會把Page裏所有的控件呈現
//依次調用Control裏所有子控件的RenderControl
foreach (Control control in children)
{
control.RenderControl(writer);
}
}
}
以上是Asp.net 的Render處理
肯定有人要問上面的if是幹嗎的,這個可是微軟流的接口呀,整個asp.net ajax全靠這個接口進行處理
在Asp.netAjax1.0上,你肯定要放一個ScriptManager吧
它也是Control的子類,
在Page生命週期的OnPreRender時,它會調用
PageRequestManager.OnPreRender();
問題就在這,PageRequestManager.OnPreRender()實現如下
_owner.IPage.SetRenderMethodDelegate(RenderPageCallback);
SetRenderMethodDelegate()方法是幹什麼用的
在Control中,當您調用了SetRenderMethodDelegate會給Control.RareFieldsEnsured設置值
public void SetRenderMethodDelegate(RenderMethod renderMethod)
{
//使用OccasionalFields.RareField
this.RareFieldsEnsured.RenderMethod = renderMethod;
this.Controls.SetCollectionReadOnly("Collection_readonly_Codeblocks");
}
到這一切就清晰了,當您設置了RenderPageCallback,正常的Asp.net流程就不會再走了,所有的子控件就不會被Render
在Asp.netAjax 1.0中使用
private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)
{
...
//取出當前的HtmlForm
IHtmlForm formControl = _owner.IPage.Form;
//手動設置當FormControl.RenderControl時()回調方法 ,因爲這個時候Page.Controls裏有子控件不會被Render啦,怎麼辦
//手動處理一個唄
formControl.SetRenderMethodDelegate(RenderFormCallback);
//同樣,HtmlForm.RenderControl不會在Page.Controls裏自動觸發的
//這個時候您需要手動的RenderCtronl一下,這樣才能調用RenderFormCallback
formControl.RenderControl(formWriter);
//生成客戶端回調信息|asyncPostBackControlIDs
EncodeString(writer, AsyncPostBackControlIDsToken, String.Empty, GetAsyncPostBackControlIDs(false));
//生成客戶端回調信息 |postBackControlIDs
EncodeString(writer, PostBackControlIDsToken, String.Empty, GetPostBackControlIDs(false));
//生成客戶端回調信息 |updatePanelIDs|tUpdatePanel
EncodeString(writer, UpdatePanelIDsToken, String.Empty, GetAllUpdatePanelIDs());
//生成客戶端回調信息 |childUpdatePanelIDs
EncodeString(writer, ChildUpdatePanelIDsToken, String.Empty, GetChildUpdatePanelIDs());
//生成客戶端回調信息 |panelsToRefreshIDs
EncodeString(writer, UpdatePanelsToRefreshToken, String.Empty, GetRefreshingUpdatePanelIDs());
//生成客戶端回調信息 |asyncPostBackTimeout
EncodeString(writer, AsyncPostBackTimeoutToken, String.Empty, _owner.AsyncPostBackTimeout.ToString(CultureInfo.InvariantCulture));
........
}
private void RenderFormCallback(HtmlTextWriter writer, Control containerControl)
{
....
//呈現所有的UpdatePanel
if (_updatePanelsToRefresh != null)
{
foreach (UpdatePanel panel in _updatePanelsToRefresh)
{
if (panel.Visible)
{
panel.RenderControl(_updatePanelWriter);
// 會生成如何的客戶端回調信息
//169|updatePanel|UpdatePanel1|/r/n
}
}
}
...
//依次將頁面上所有控件的RenderControl,寫入dummyWriter
foreach (Control control in containerControl.Controls)
{
control.RenderControl(dummyWriter);
//生成一部分信息
<input type=/"submit/" name=/"Button1/" value=/"Button/" id=/"Button1/" />/r/n
<input name=/"txt/" type=/"text/" value=/"wxy/" id=/"txt/" />/r/n
}
注意,在RenderFormCallback時,HttpRequest會手動的Flush()將上面的信息刷加IIS
}
至於Ctronl.Render時,是如何寫入HtmlTextWriter,再寫入HttpWriter,
再寫回HttpWorkRequest,最終刷回內核,或使用socket發送回服務器,不在本文討論範圍.......
由上可見,Asp.net Ajax,必沒有使用傳統的HttpRequest.Filter來處理回發信息,而是使用了Asp.net 2.0內置的方式和回調的
Asp.net Ajax回調後呈現到客戶端有二個部分,一部分是由RenderFormCallback生成的如下
RenderFormCallback部分會生成如下的客戶端腳本
169|updatePanel|UpdatePanel1|/r/n
<input type=/"submit/" name=/"Button1/" value=/"Button/" id=/"Button1/" />/r/n
<input name=/"txt/" type=/"text/" value=/"wxy/" id=/"txt/" />/r/n
一部分是RenderPageCallback生成的
52|hiddenField
|__VIEWSTATE|/wEPDwUJNjg1NjA3NDcxZGRCX32AH2I/NEq+gCFrVr49kQDECw==|56
|hiddenField|__EVENTVALIDATION|/wEWAwKf344xAoznisYGApKGsMIJOfjKisKPYP0O2NXUpNjnA29hMlA=|0
|asyncPostBackControlIDs|||0|postBackControlIDs|||13|updatePanelIDs||tUpdatePanel1|0
|childUpdatePanelIDs|||12|panelsToRefreshIDs||UpdatePanel1|2|asyncPostBackTimeout||90|12
|formAction||Default.aspx|13|pageTitle||Untitled Page|
在客戶端的部分Sys.WebForm._onFormSubmitCompleted()會對所有的信息以Json type:type,id:id,content:content進行封裝
取出UpdatePanel,設置其innerhtml,客戶端詳細部分見趙老大的blogs