日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Web Control 开发系列(三) 解析IPostBackEventHandler和WebForm的事件机制

發布時間:2025/5/22 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Web Control 开发系列(三) 解析IPostBackEventHandler和WebForm的事件机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

WebForm最大的魅力大概就是它自己的一套事件處理機制了,要做一個好的Control,必須深入理解這套機制,只有這樣才可以讓我們的Control有一整套Professional的Event,而IPostBackDataHandler和IPostBackEventHandler是實現事件機制的核心接口,在我的上一篇文章(Web Control 開發系列(二) 深入解析Page的PostBack過程和IPostBackDataHandler)中已經介紹了IPostBackDataHandler的實現原理,本章主要介紹IPostBackEventHandler.最后進行總結,來看看Web Control整個事件機制的全貌。
?????? 要了解IPostBackEventHandler接口,首先就必須了解.net Framework的腳本注冊原理和一些重要的內置腳本。
?? 一、腳本注冊 ??
????? .net Framework可以在我們毫不知情的情況下,根據我們在服務端對Control屬性的設置,在ControlRender的時候,根據需要動態向客戶端注冊腳本和Hidden的<input>元素,Hidden的<input>用來在客戶端保存一些重要的信息,而腳本是用來完成一些邏輯行為的控制。我們先看看.net Framwork是如何實現腳本和Hidden <input>的.
?????
????? WebForm編程過程中,如果我們希望向客戶端輸出腳本或者一些Hidden的<input>元素,我們通常是通過 Page.ClientScript對象完成的,這個對象是一個ClientScriptManager類型的實例,我們一般(也有特殊情況)在Control.OnPreRender()方法里面調用Page.ClientScript.RegisterHiddenFiled或者Page.ClientScript.RegisterStartScript,還可以獲得一些內置的腳本,比如 Page.ClientScript.GetPostBackEventReference,這些方法的調用都會記錄一些標記數據,真正輸出到客戶端,是在Page的Render方法調用的時候,而完成輸出的是下面兩個方法:
??
?? Page.BeginFormRender 和Page.EndFormRender
???? 這兩個方法會在HtmlForm.RenderChildren里面調用,用來給<form>的開始和結束位置添加一些腳本和hidden field。具體完成的功能有:

??? * Render所有Register的Hidden Fields,同時也Render用來保存ViewState的Hidden Field
??? * Render用來保存當前 <form>的滾動位置的Hidden Field和Start Javascript
??? * Render控制當前焦點的Focus.js腳本引用語句
??? * Render用來執行回調的__doPostBack()函數,僅僅在相關標記打開的時候才會Render.
??? * Render用來執行PostBack的WebForms.js腳本引用語句。這個文件主要包含了WebForm常用的腳本,有PostBack的腳本和CallBack的腳本。
??? * Render已經注冊的腳本塊(Script Block)
??? * Renderl Client Startup Script(啟動即執行的腳本)

__doPostBack()腳本
Render出來的__doPostBack()如下:

<script?type="text/javascript">
//<![CDATA[
var?theForm?=?document.forms['form1'];
if?(!theForm)?{
????theForm?
=?document.form1;
}

function?__doPostBack(eventTarget,?eventArgument)?{
????
if?(!theForm.onsubmit?||?(theForm.onsubmit()?!=?false))?{
????????theForm.__EVENTTARGET.value?
=?eventTarget;
????????theForm.__EVENTARGUMENT.value?
=?eventArgument;
????????theForm.submit();
????}

}

//]]>
</script>

其實一旦用戶注冊了__doPostBack函數,兩個配套的Hidden字段也會同步注冊

<input?type="hidden"?name="__EVENTTARGET"?id="__EVENTTARGET"?value=""?/>
<input?type="hidden"?name="__EVENTARGUMENT"?id="__EVENTARGUMENT"?value=""?/>

上面的代碼就是先把Event target和Event Argument存入指定的Hidden Field中,然后調用<form>的submit方法來提交數據。這段腳本是Framework提供的最普通的引發PostBack的腳本,我們寫Control的時候可以通過Page.ClientScript.GetPostBackEventReference來獲得這個腳本(注意,這個方法有好幾個重載的版本,其中當選用了一些參數的時候,也可能獲得另一個腳本WebForm_DoPostBackWithOptions,下面將介紹)。

WebForm_DoPostBackWithOptions腳本
還有一個比較重要的腳本就是WebForm_DoPostBackWithOptions,它的作用比__doPostBack更強,比如對于支持CauseValidation屬性的Control,如Checkbox,TextBox,這些Control當CauseValidation為true的時候,它就會在onclick屬性里面Render出WebForm_DoPostBackWithOptions腳本。這樣可以在調用theForm.submit()方法之前執行當前Form的所有Validator的客戶端校驗。
????? 所以事實上,WebForm_DoPostBackWithOptions是包容__doPostBack函數的,凡是注冊了WebForm_DoPostBackWithOptions的地方,必須注冊__doPostBack,因為WebForm_DoPostBackWithOptions里面在執行完很多附加功能的代碼(如對所有的Validator進行校驗,控制Focus在沒有通過校驗的Control等)后,如果一切正常并且需要做ClientSubmit(Button當UseSumitBehavior為true和ImageButton不使用Client Script Submit,它們自己提交,因為它們自己輸出的就是可以觸發<form>提交的<input>元素),就調用__doPostBack。只有調用__doPostBack才會給__EVENTTARGET和__EVENTARGUMENT設置值,所以Button(UseSumitBehavior為true)和ImageButton引起的回傳的時候,我們觀察__EVENTTARGET和__EVENTARGUMENT,會發現都為“”。

二、使用腳本進行PostBack的Control分析

注意首先要說明的一點是在HTML的表單域里面,具備自動讓<form>想服務器端發送數據,引起回傳的只有兩個:
?? <input type="submit">
?? <input type="image">

Button
這是個特殊的Control,因為它們本身具備了自動調用<form>submit的能力,Button上面有一個屬性叫UseSubmitBehavior,這個屬性用來控制生成的<Input>的type是"submit"還是"button",如果為true,那么就是"submit".
1. 如果CauseValidation為false,UserSubmitBehavior為true,那么意味著僅僅進行提交,并不校驗,所以這個時候,生成的代碼是

<input?type="submit"?name="Button1"?value="Button"?id="Button1"?/>

沒有任何onclick的腳本調用。
2. 如果CauseValidation為true, UseSubmitBehavior為true,那么生成的代碼是

<input?type="submit"?name="Button1"?value="Button"?onclick="javascript:WebForm_DoPostBackWithOptions(new?WebForm_PostBackOptions(&quot;Button1&quot;,?&quot;&quot;,?true,?&quot;&quot;,?&quot;&quot;,?false,?false))"?id="Button1"?/>

因為要執行校驗,所以必須調用WebForm_DoPostBackWithOptions方法。
3. 如果CauseValidation為false,UseSubmitBehavior為false,那么生成的代碼是

<input?type="button"?name="Button1"?value="Button"?onclick="javascript:__doPostBack('Button1','')"?id="Button1"?/>

這個時候,因為type是“button”,它不具備submit數據的能力,所以只能用腳本幫助解決,同時不需要校驗,所以就使用__doPostBack函數,如果CauseValidataion為true,onclick里面的函數就為WebForm_DoPostBackWithOptions,因為__doPostBack不具備客戶端校驗的能力。

ImageButton
???? imageButton 比較特別的是同時實現了IPostBackDataHandler, IPostBackEventHandler, 在Asp.net2.0里面只有兩個Control同時實現了這兩個接口,一個是ImageButton,一個是HtmlInputImage。這兩個Control最后生成的都是<input type="image">.
???? 對于這個HTML元素,它在點擊的時候,會引起<form>submit數據,同時會把當前點擊的位置作為兩個表單域被<form>收集。比如:<input type="image" name="myImageButton">,那么點擊到(20,100)的時候,<form>表單域里面會多出兩個:["myImageButton.x"] = "20",["myImageButton.y"] = "100".
???? 通過上面的介紹,我們知道<input type="image">提交的數據和它的Name并不一致,根據我上一篇文章(Web Control 開發系列(二) 深入解析Page的PostBack過程和IPostBackDataHandler)的介紹,我們知道它必須使用注冊的方法才能保證page在處理Request的時候調用當前Control的IPostBackDataHandler接口。具體做法就是在PreRender的時候調用Page.RegisterRequiresPostBack(this).
??? 同上面介紹的Button一樣,當CauseValidation為true的時候,會給輸出加一個οnclick="WebForm_DoPostBackWithOptions"的屬性設置,這樣在提交前可以使用腳本進行校驗。否則就不會生成腳本。

CheckBox,TextBox,RadioButton,ListControl及其派生類 等
??? 這些Control輸出的Html元素都沒有自動submit的能力,所以這些Control普通情況下是不會引發回傳的,但是為了方便用戶,.net Framework在上面暴露了一個屬性叫AutoPostBack,一旦這個屬性為true,就表示這些Control具備了引發回傳的能力,具體怎么實現回傳呢,還是依賴于上面介紹的兩段腳本。
???? 當AutoPostBack為true,CauseValidation為true的時候就注冊WebForm_DoPostBackWithOptions. 當AutoPostBack為true,CasuseValidataion為false的時候就注冊 __doPostBack函數,同時在AutoPostBack屬性為true的時候,為了防止性能問題,一般注冊的腳本都用setTimeout(函數名,0)包起來,這樣可以認為是一個模擬的異步調用(事實JavaScript是單線程的,這樣的調用會自動進入調用隊列,等待執行,不會阻塞現在的調用)。

三、服務器端處理
????? 通過上面的分析知道,.net Framework一般是通過注冊腳本,在腳本里面調用theForm.submit()來進行提交的,這樣就形成了一次PostBack,而可以導致回傳的兩個重要的腳本也已經在上面介紹了。那么當腳本導致回傳了,服務器端是如何處理請求并引發Control的事件的呢?
????? 通過我第一篇文章(Web Control 開發系列(一) 頁面的生命周期)的介紹,我們知道在頁面Load階段結束后,如果Page.IsPostBack,我們會先進入 IPostBackDataHandler的處理階段,然后才進入IPostBackEventHandler處理階段。我們下面分析的就是 IPostBackEventHandler處理階段的邏輯。這個邏輯是通過 Page.RaisePostBackEvent(NameValueCollection postData)進入的。
????? 在這個函數的處理里面有好幾種情況:
????? 1. 其中最普通的一種處理是通過postData["__EVENTTARGET"]postData[“__EVENTARGUMENT”]拿到相應的值,這些值都是在<form>提交前通過腳本設置上去的,然后通過Page.FindControl來找到合適的Control,這樣就可以取到Control.PostBackEventHandler,然后調用 IPostBackEventHandler.RaisePostBackEvent方法,就導致Control的服務端事件被觸發。
?????? 2. 還有一種情況,就是服務器端的Control是Button或者ImageButton,它們的提交是通過Html元素自己的能力,所以提交發生的時候,沒有任何腳本調用,自然postData["__EVENTTARGET"]和 postData[“__EVENTARGUMENT”]都為"",這個時候我們如何找到引發PostBack的Control并且調用它的 IPostBackEventHandler接口的方法呢?
?????????? 這就要利用另一種發事件的機制——注冊機制。這個機制主要通過 Page.RegisterRequiresRaiseEvent(IPostBackEventHandler control)函數實現的,這個函數在Asp.net2.0中有三個地方調用:

  • ???? HtmlInputImage.LoadPostData,
  • ???? ImageButton.LoadPostData,
  • ???? Page.ProcessPostData

???? 這三個地方的調用都是在處理PostBackData階段,因此我們可以認為這個注冊機制最好在處理PostBackData階段使用是比較符合規范的。
???? 對于HtmlInputImage和ImageButton這兩個Control,它們都有PostBackData,而且通過注冊的方法實現了IPostBackDataHandler接口,所以在LoadPostData階段調用Page.RegisterRequiresRaiseEvent,這樣就顯式的告訴Page在PostBackEvent處理階段調用自己的IPostBackEventHandler接口,就實現了服務端Click事件的觸發。

???? 那么Page.ProcessPostData函數(在我的上一篇文章Web Control 開發系列(二) 深入解析Page的PostBack過程和IPostBackDataHandler有介紹),它會收集所有的表單提交數據,如果有和這個數據對應的Control(通過Page.FindControl查找),那么就設法調用其IPostDataHandler,如果IPostDataHandler為null,那么設法取其IPostEventHandler,如果不為null,那么就調用Page.RegisterRequiresRaiseEvent函數來注冊它。Button只實現了IPostBackEventHandler接口,沒有實現IPostBackDataHandler接口,所以就通過這種發式來觸發事件的。

???? 一旦在Page上進行了Page.RegisterRequiresRaiseEvent注冊,系統就不會關心postData["__EVENTTARGET"]和postData["__EVENTARGUMENT"]了,直接就調用注冊的IPostBackEventHandler.RaisePostBackEvent方法。

上面介紹的內容都是對Page.RaisePostBackEvent的分析:

private?void?RaisePostBackEvent(NameValueCollection?postData)
????????
{
????????????
//?1.?假如已經在Page上顯式的注冊了引起PostBackEvent的Control,就直接處理
????????????if?(this._registeredControlThatRequireRaiseEvent?!=?null)
????????????
{
????????????????
this.RaisePostBackEvent(this._registeredControlThatRequireRaiseEvent,?null);
????????????}

????????????
else
????????????
{
????????????????
//?這部分代碼,我自己按照Reflector反編譯的結果重新組織了,但是邏輯
????????????????
//?沒有任何變化,只是方便閱讀理解

????????????????
//?2.?假如沒有注冊,就查找__EVENTTARGET記錄的Control來處理
????????????????string?str?=?postData["__EVENTTARGET"];
????????????????
bool?flag?=?!string.IsNullOrEmpty(str);
????????????????Control?control?
=?null;
????????????????
if?(flag)
????????????????
{
????????????????????control?
=?this.FindControl(str);
????????????????????
if?((control?!=?null)?&&?(control.PostBackEventHandler?!=?null))
????????????????????
{
????????????????????????
string?eventArgument?=?postData["__EVENTARGUMENT"];
????????????????????????
this.RaisePostBackEvent(control.PostBackEventHandler,?eventArgument);
????????????????????}

????????????????}

????????????????
else?if?(this.AutoPostBackControl?==?null)
????????????????
{
????????????????????
//?這個AutoPostBackControl的標記設置為了不重復做Validate,后面我在講述
????????????????????
//?Validation機制的時候會介紹
????????????????????this.Validate();
????????????????}

????????????}

????????}


四、Composite Control 的冒泡事件
??????????? 在Control上面有一個方法RaiseBubbleEvent,這個方法就是沿著Control Tree向上一次調用OnBubbleEvent函數,知道返回true,就推出,是一個典型的冒泡事件。Control對于OnBubbleEvent的實現是簡單的返回false,也就是說如果我們不做處理,那么事件會不停的向上冒泡知道最頂端的Page。

protected?void?RaiseBubbleEvent(object?source,?EventArgs?args)
{
????
for?(Control?control?=?this.Parent;?control?!=?null;?control?=?control.Parent)
????
{
????????
if?(control.OnBubbleEvent(source,?args))
????????
{
????????????
return;
????????}

????}

}


???? 我們知道了這個冒泡的機制,那么冒泡的源頭在哪里呢??這就是我們做Control的人要考慮的,如果我們希望我們的Control的Event支持冒泡,那么我們就應該在Control的Event發生的時候調用RaiseBubbleEvent這個函數,這樣當別人在一個復合控件里面使用我們的Control的時候,它就可以在外面接收到我們Control發的冒泡事件,目前調用了這個冒泡函數的Control有

???? 從上面,我們最值得注意的是有三個簡單Control實現了向上冒泡:Button, ImageButton, LinkButton,其它的都是一些復合Control在OnBubbleEvent里面進行二次冒泡。因此如果我們做一個復合的Control,我們可以在最外層的OnBubbleEvent函數里面監聽這個Control內部的所有的Button,ImageButton,LinkButton的事件。

五、總結
???? 所有WebForm事件的根源依賴于Form的submit()執行而引起PostBack(CallBack這里不考慮),而引起PostBack主要依賴于Html Input (type="image" or "submit")元素和腳本。
???? 然后在PostBack階段分析數據,如果數據變化可以Raise相關的Event,如果客戶端記錄了誰發了Event,也可以發Event。如果想讓Event冒泡,就call RaiseBubbleEvent
????

轉載于:https://www.cnblogs.com/mcsm/articles/1869632.html

總結

以上是生活随笔為你收集整理的Web Control 开发系列(三) 解析IPostBackEventHandler和WebForm的事件机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。