ASP.NET進階:認清控制元件 之 Button (轉)

iDotNetSpace發表於2010-10-15
“做.NET不值錢,沒有技術含量,拖拖控制元件就行了。”———— 這類言語我最討厭了,一點技術含量都沒有,不懂.NET就瞎嚷嚷。不管做什麼,做好都不容易,你沒做過怎麼知道沒有技術含量呢? 我們既享受控制元件帶來的快捷,也知其原理,甚至可以自己開發控制元件,你還能說我們沒技術含量?!

控制元件是什麼? 是.NET Framework對Html裡的元素的封裝。 他把一個變成了 Button類,把變成了Label類,把變成了DropDownList。這就是控制元件,就是這些類。

上節我們提到了Page類,Page的生命週期裡有些事件是和處理控制元件類有關的。

PreInit
使用該事件來執行下列操作:
檢查 IsPostBack 屬性來確定是不是第一次處理該頁。
建立或重新建立動態控制元件。
動態設定主控頁。
動態設定 Theme 屬性。
讀取或設定配置檔案屬性值。
Render
這不是事件;在處理的這個階段,Page 物件會在每個控制元件上呼叫此方法。所有 ASP.NET Web 伺服器控制元件都有一個用於寫出傳送給瀏覽器的控制元件標記的 Render 方法。
如果建立自定義控制元件,通常要重寫此方法以輸出控制元件的標記。不過,如果自定義控制元件只合並標準的 ASP.NET Web 伺服器控制元件,不合並自定義標記,則不需要重寫 Render 方法。
一個是初始化,一個是顯示,其他的還有事件之類的,我們暫且不提。我們可以看出,初始化其實就是例項化desinger.cs裡用到的控制元件。而最終這些控制元件都需要轉換成HTML標籤,也就是被Page呼叫其Render方法。Page自身也是控制元件所,以Page的Render就是負責生成HTML的。

控制元件類都在System.Web.UI.WebControls下,我們可以開啟MSDN檢視一些常用控制元件的類和其相關的事件方法。如果有用Refletor的話,可以開啟System.Web.dll檢視更加詳細的原始碼。你會發現有意思的繼承關係,理解MS的設計理念,也更方便我們對控制元件有個更加清晰的認識。

Button類。
當我們在FORM裡放置一個Button,該按鈕預設為Submit按鈕,如果設為UseSubmitBehavior. = false,就可以看到客戶端會生成該按鈕需要的JS程式碼。
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --&gt    <div class="aspNetHidden">
    
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
    
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
    
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTcxODU4OTc0MWRk6Htqf5QF2exQx/BOtdDx1djNfbW7y3bPbmRizhypOr4=" />
    
div>

    
<script type="text/javascript">
    
//
    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>
    
<input type="submit" name="Button1" value="Button" id="Button1" />
    
<input type="button" name="Button2" value="Button" onclick="javascript.:__doPostBack('Button2','')" id="Button2" />

 

__doPostBack就是出發form的提交,而Button2呼叫的時候傳值了自己的名字,然後__EVENTTARGET的值被設為了Button2,這樣在提交的時候,Post的資料裡就會告訴服務端是Button2觸發的!再看Button1卻沒有呼叫__doPostBack,因為他是submit型別,在提交的時候瀏覽器會把他的名字預設提交過去,同時其他的按鈕不會被提交。

所以,既然服務端知道了是哪個按鈕被點過,自然就會呼叫該按鈕例項的OnClick方法。看看Page類裡有一個方法。
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --&gtif (this.IsPostBack)
{
    
this.RaisePostBackEvent(this._requestValueCollection);
}

private void RaisePostBackEvent(NameValueCollection postData)
{
     
if (this._registeredControlThatRequireRaiseEvent != null)
     {
         
this.RaisePostBackEvent(this._registeredControlThatRequireRaiseEvent, null);
     }
     
else
     {
         
string str = postData["__EVENTTARGET"];
         
bool flag = !string.IsNullOrEmpty(str);
         
if (flag || (this.AutoPostBackControl != null))
         {
     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
         {
     
this.Validate();
         }
     }
}

 

啊哈!明白沒有?在IsPostBack(也就是提交)後,呼叫了觸發所有控制元件事件的方法。postData["__EVENTTARGET"]明顯就是我們剛才的點的按鈕名稱,然後通過FindControl得到該物件,最後呼叫RaisePostBackEvent方法出發該物件的事件。再往下看具體流程:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --&gt[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
{
   sourceControl.RaisePostBackEvent(eventArgument);
}

  也就是說,Button類必須實現IPostBackEventHandler介面(因為Page不知道控制元件到底是什麼,所以要使用介面,這就是介面的用處)。

看看具體實現:
<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--&gt    protected virtual void RaisePostBackEvent(string eventArgument)
    {
         
base.ValidateEvent(this.UniqueID, eventArgument);
         
if (this.CausesValidation)
         {
             
this.Page.Validate(this.ValidationGroup);
         }
         
this.OnClick(EventArgs.Empty);
         
this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
    }

 

首先是驗證,然後是執行OnClick,然後執行OnCommand。呵呵,一個OnClick我們終於走完了~ 感覺如何? 頭漲了沒?

看下Button類,確實繼承IPostBackEventHandler介面。
<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--&gt    [Designer("System.Web.UI.Design.WebControls.ButtonDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), SupportsEventValidation, ToolboxData(""server\" Text=\"Button\">{0}:Button>"), DefaultEvent("Click"), DefaultProperty("Text"), DataBindingHandler("System.Web.UI.Design.TextDataBindingHandler, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    
public class Button : WebControl, IButtonControl, IPostBackEventHandler
    {
    ....
        [WebCategory(
"Action"), WebSysDescription("Button_OnClick")]
        
public event EventHandler Click;
        
protected virtual void OnClick(EventArgs e)

        [WebCategory(
"Action"), WebSysDescription("Button_OnCommand")]
        
public event CommandEventHandler Command;
    
        [WebSysDescription(
"Button_Text"), WebCategory("Appearance"), Bindable(true), Localizable(true), DefaultValue("")]
        
public string Text { getset; }
    ....
    }

 

並且看到熟悉的Click,Command,Text,而ID,Width,Height則在他的父類中。

看看OnClick是幹嘛的?
<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--&gt    protected virtual void OnClick(EventArgs e)
    {
        EventHandler handler 
= (EventHandler) base.Events[EventClick];
        
if (handler != null)
        {
         handler(
this, e);
        }
    }

 

OnClick的方法是被點選後執行的方法,具體是從Events裡找到EventClick型別的委託,其實就是判斷Click事件是否被訂閱,如果有該型別委託,則表明已經被訂閱,便執行該委託例項(也就是我們定義的Click具體方法)。

再來看下是如何訂閱的:
<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--&gt    [WebCategory("Action"), WebSysDescription("Button_OnClick")]
    
public event EventHandler Click
    {
        add
        {
         
base.Events.AddHandler(EventClick, value);
        }
        remove
        {
         
base.Events.RemoveHandler(EventClick, value);
        }
    }

 

Click事件具有add和remove兩個類似屬性的get和set的方法,這當然是C#的特定語法,實際編譯後的IL裡是兩個小方法。
Add是訂閱,remove是取消訂閱。AddHandler就是把具體的方法和委託型別新增到Events裡。
<!--Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--&gt    public void AddHandler(object key, Delegate value)
    {
         ListEntry entry 
= this.Find(key);
         
if (entry != null)
         {
             entry.handler 
= Delegate.Combine(entry.handler, value);
         }
         
else
         {
             
this.head = new ListEntry(key, value, this.head);
         }
    }

 

一氣呵成,我們知道了Click的具體呼叫實現,且明白的事件的訂閱。後面再說控制元件的其他屬性,休息休息。後面會介紹Render和DataBind :) 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-676022/,如需轉載,請註明出處,否則將追究法律責任。

相關文章