上一篇文章介紹了實作自訂伺服器控制項事件的基本概念。本文將透過典型範例講解捕捉回傳事件的實作方法。
1. 實作擷取回傳事件
如果伺服器控制項需要擷取來自客戶端的回傳事件,並想為該回傳事件自訂伺服器端事件處理邏輯,那麼控制項必須實作System.Web.UI.IPostBackEventHandler介面。下面列舉了該介面定義。
public interface IPostBackEventHandler
{
void RaisePostBackEvent(string eventArgument);
}
如上程式碼所示,IPostBackEventHandler介面僅包含一個成員方法RaisePostBackEvent。此方法使伺服器控制項能夠處理將窗體傳送到伺服器時引發的事件,其參數eventArgument表示要傳遞到事件處理程序的可選事件參數。開發人員可以在RaisePostBackEvent方法中實作伺服器控制項回傳過程中執行的邏輯。一般情況下,RaisePostBackEvent方法將引發一個或多個伺服器端事件。以下程式碼片段顯示了在伺服器上引發Click事件的RaisePostBackEvent實作。
public void RaisePostBackEvent(String eventArgument)
{
OnClick(EventArgs.Empty);
}
實作擷取回傳事件並不是僅僅使伺服器控制項類別實作IPostBackEventHandler接口,並實作該介面成員方法就可以的。開發人員還需要注意實現其他內容。以下列舉了實作捕捉回傳事件過程中的三個要點。
第一,也是最重要的,即自訂伺服器控制項必須實作IPostBackEventHandler接口,並實作該介面成員RaisePostBackEvent方法。這過程在上文中已經進行了介紹。
第二,為控制項指派UniqueID。
定義造成回傳事件的控制項的name屬性值為UniqueID,是正確實作RaisePostBackEvent方法的關鍵之一。當引發回傳後,頁框架就會搜尋傳送的內容,並決定傳送物件的名稱是否與實作IPostBackEventHandler的伺服器控制項的UniqueID對應。如果對應,頁框架就會在該控制項上呼叫RaisePostBackEvent方法。這裡的重點是需要開發人員在呈現邏輯中,為控制項的name屬性指派UniqueID。下面列舉了一個簡單的程式碼範例。
protected override void Render(HtmlTextWriter output)
{
output.Write("<INPUT TYPE=submit name="+this.UniqueID+"Value='Click Me' />");
}
如上程式碼所示,在控制項呈現方法Render中,呈現了一個按鈕,其name屬性值為UniqueID。只有為造成回傳的控制項的name屬性指派了UniqueID,才能夠正確實作擷取回傳事件。
第三,實現事件屬性結構。
事件屬性結構是一種最佳化的事件實作方式。在介紹之前,我們先來看看常見的控制項事件實作方式。具體代碼如下所示。
.....
public class WebCustomControl:WebControl,IPostBackEventHandler{
//聲明Click事件委託public event EventHandler Click;
//實作RaisePostBackEvent方法void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
OnClick(EventArgs.Empty);
}
//定義OnClick事件處理程序protected virtual void OnClick(EventArgs e) {
if(Click != null) { Click(this,e); }
}
.....
}
在上述程式碼中,包含了與事件定義相關的三個關鍵內容:一、定義Click事件委託;二、控制項類別實作了IPostBackEventHandler接口,其中當實作介面成員方法RaisePostBackEvent過程中,定義了事件處理程序OnClick ;三、實作OnClick事件處理程序。以上實作方法簡單易用,然而卻存在著一個缺點,就是執行效率低。尤其是在一個類別中引發多個事件的情況下,將會增加開銷,浪費大量伺服器資源,最終導致運作效率降低。
為了解決以上問題,以下介紹一個最佳化的事件實作方式--事件屬性結構。這個結構使用System.ComponentModel.EventHandlerList類,這個類別提供一個簡單的委託清單。透過使用該類別所提供的相關方法,開發人員能夠靈活的操作控制項的事件處理程序委託清單。例如,控制項中的Click事件,使用事件屬性結構如下:
protected static readonly object EventClick = new object();
public event EventHandler Click{
add {
Events.AddHandler(EventClick,value);
}
remove {
Events.RemoveHandler(EventClick,value);
}
}
在事件屬性結構定義之前,首先需要定義Click事件委託物件。由於每個事件僅建立一次,因此,需要聲明為靜態和唯讀的。然後,在屬性結構中透過AddHandler、RemoveHandler方法操作事件處理程序委託清單。當頁面呼叫Click事件時,它會向控制項的EventHandlerList集合中新增或刪除處理程序。由於這種實作方法,在多個事件的聲明過程中比普通的實作方法效率高,因此是非常值得推薦的方法。
另外,在OnClick方法的實作過程中,當用一個事件屬性時,必須從EventHandlerList中取回委託,並將其轉換成EventHandler的類型。
protected virtual void OnClick(EventArgs e){
EventHandler clickHandler = (EventHandler)Events[EventClick];
if(clickHandler != null) {
clickHandler(this,e);
}
}
請讀者註意:事件屬性結構不適用於VB.NET語言,只能在C#等語言中應用。
2. 典型應用
實事求是的講,以上捕獲回傳事件的理論介紹對於從未實現過伺服器控制項事件的讀者而言,有些難以理解。為此,本小節透過一個典型的範例來具體說明捕捉回傳事件的實作方法。
本例實作了一個自訂伺服器控制項WebCustomControl。此控制項雖然呈現為按鈕外觀,但其並不是從Button類別繼承而來。當單擊該按鈕時,控制項將引起回傳,伺服器端自動捕獲回傳的單擊事件,並且引發Click事件,執行對應事件處理程序。下面是伺服器控制項實作的源碼代碼:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace WebControlLibrary{ [DefaultEvent("Click")] [ToolboxData("<{0}:WebCustomControl runat=server></{0}:WebCustomControl>")] public class WebCustomControl : WebControl, IPostBackEventHandler { // 定義一個Click事件委託物件private static readonly object EventClick = new object(); //實作Click事件屬性[Description("Click事件屬性"), Category("Action") ] public event EventHandler Click { add { Events.AddHandler(EventClick, value); } remove { Events.RemoveHandler(EventClick, value); } } // 重寫控制項呈現方法RenderContents protected override void RenderContents(HtmlTextWriter output) { output.Write("<input type='submit' name=" + this.UniqueID + " value=請點選/>"); } //實作事件方法protected virtual void OnClick(EventArgs e) { EventHandler clickHandler = (EventHandler)Events[EventClick]; if (clickHandler != null) { clickHandler(this, e); } } // 實作IPostBackEventHandler介面成員void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) { OnClick(EventArgs.Empty); } } } |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ Register TagPrefix="cc" Namespace="WebControlLibrary" Assembly="WebControlLibrary" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> void wcc1_Click(object sender, EventArgs e) { message.Text = "您剛剛點擊了上面的按鈕"; } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>捕獲回傳事件</title> </head> <body> <form id="form1" runat="server"> <center> <cc:WebCustomControl ID="wcc1" runat="server" OnClick="wcc1_Click" /> <br /> <br /> <asp:Label ID="message" runat="server"></asp:Label> </center> </form> </body> </html> |
圖1 頁面初始化效果圖2 點選按鈕後的效果圖 |
//定義屬性AutoPostBack public bool AutoPostBack{ set { this._autoPostBack = value; } get { return this._autoPostBack; } } //在Render方法中加入Page.GetPostBackEventReference()方法 protected override void Render(HtmlTextWriter output){ ..... if(this.AutoPostBack) { writer.WriteAttribute("ontextchanged","javascript:" + Page.GetPostBackEventReference(this)); } ..... } |