定製自己的委託和事件引數類
本文節選自《庖丁解牛:縱向切入ASP.NET 3.5控制元件和元件開發技術》一書
一般在實際開發中,對於事件不需要傳遞資料資訊時,像上面的KingTextBox控制元件的事件,在引發事件時傳遞的引數為EventArgs.Empty,如下所示:
OnTextChanged(EventArgs.Empty);
這是因為控制元件KingTextBox的TextChanged事件比較簡單,這裡不需要引數物件傳遞資料。但像一些複雜的控制元件比如GridView的按鈕命令事件,必須得有引數命令表示單擊了哪個按鈕;翻頁事件,需要EventArgs引數物件把當前頁資訊傳遞到頁面後臺程式碼的事件體的第二個引數中,然後開發人員根據此頁引數從資料庫取得對應頁的資料;還有像ItemDataBound事件,也需要把當前Row資訊和索引等資料作為EventArgs引數傳遞到事件例項中。
當遇到以上這些情況,我們就需要定義自己的事件引數類和委託,而不使用預設的System.EventArgs類。下面就以Grid控制元件的翻頁功能說明一下定義事件引數類和委託的方法,請看以下程式碼:
/// <summary>
/// 獲得本書更多內容,請看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
public delegate void GridPageChangedEventHandler(object source, GridPage ChangedEventArgs e);
public class GridPageChangedEventArgs : System.EventArgs
{
public GridPageChangedEventArgs()
{
}
private int intCurrentPageIndex;
public new int CurrentPageIndex
{
get { return intCurrentPageIndex; }
set { intCurrentPageIndex = value; }
}
private int intPageCount;
public new int PageCount
{
get { return intPageCount; }
set { intPageCount = value; }
}
private int intPageSize;
public new int PageSize
{
get { return intPageSize; }
set { intPageSize = value; }
}
}
以上程式碼定義了一個儲存翻頁相關資訊的引數類,此引數類派生於System.EventArgs類,在這裡系統不強制要求一定繼承於該類,不繼承它也是可以的,但繼承於該類有一點好處。先看一下System.EventArgs基類的程式碼:
/// <summary>
/// 獲得本書更多內容,請看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
public class EventArgs
{
//表示沒有事件資料的事件
public static readonly EventArgs Empty;
//初始化 System.EventArgs 類的新例項
public EventArgs();
}
EventArgs中除了一個構造方法外,還有一個它本身型別的Empty屬性,從這裡可以知道前面在呼叫時通過OnTextChanged(EventArgs.Empty);格式,把EventArgs.Empty作為空引數傳入事件引發方法時的用途了。這裡要說明的是如果我們的GridPageChangedEventArgs類是從EventArgs繼承而來,則不但可以傳遞GridPageChangedEventArgs類物件,而且可以使用GridPageChangedEventArgs.Empty的形式傳遞空引數物件。
在GridPageChangedEventArgs方法體中定義了一些屬性;分別表示當前頁(CurrentPageIndex)、頁總數(PageCount)和頁尺寸(PageSize)。這些都是我們自定義的頁資訊資料。
程式碼中的這句:
public delegate void GridPageChangedEventHandler(object source, GridPage ChangedEventArgs e);
定義了一個委託GridPageChangedEventHandler。該委託可以指定這樣的一個事件方法:第一個引數為object型別,第二個引數即為上面我們定義的頁引數類物件GridPageChanged EventArgs。在註冊事件時,該委託可以保證在頁面後面程式碼中自動產生的事件體的兩個引數型別與自己的兩個引數型別一致。下面是在頁面中註冊的事件後臺程式碼:
protected void Grid1_PageIndexChanged(object source,GridPageChangedEventArgs e)
{
int intCurrentPageIndex = e.CurrentPageIndex;
int intPageSize = e.PageSize;
//獲取資料邏輯
}
可以看到第二個引數型別即我們定義的GridDocPageChangedEventArgs型別,在事件方法體中,可以直接通過e.CurrentPageIndex和e.PageSize獲取資料,這種應用就比較多了。
現在頁引數物件和委託定義好了,下面說一下在主控制元件內部是如何應用它們的。宣告事件程式碼如下:
/// <summary>
/// 獲得本書更多內容,請看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
private new static readonly object EventPageIndexChanged = new object();
[Category("Action"), Description("翻頁事件")]
public event GridPageChangedEventHandler PageIndexChanged
{
add
{
base.Events.AddHandler(Grid.EventPageIndexChanged, value);
}
remove
{
base.Events.RemoveHandler(Grid.EventPageIndexChanged, value);
}
}
這裡繼續採用5.3.1小節講的高效率事件集合列表物件base.Events,事件的名稱為PageIndexChanged,委託型別為之前我們定義的委託型別GridPageChangedEventHandler。
引發事件的核心程式碼如下:
/// <summary>
/// 獲得本書更多內容,請看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
protected override bool OnBubbleEvent(object source, EventArgs e)
{
bool handled = false;
if (e is GridCommandEventArgs)
{
if ((((GridCommandEventArgs)(e)).CommandSource) is LinkButton)
{
LinkButton lb=((LinkButton)(((GridCommandEventArgs)(e)). Command Source));
if (lb.CommandName == "Page")
{
if (lb.CommandArgument == "ButtonFirst")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex != 0)
{
this.CurrentPageIndex = 0;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
if (lb.CommandArgument == "ButtonNext")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex < this.PageCount - 1)
{
this.CurrentPageIndex += 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
if (lb.CommandArgument == "ButtonPrev")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex > 0)
{
this.CurrentPageIndex -= 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
if (lb.CommandArgument == "ButtonLast")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex != this.PageCount - 1)
{
this.CurrentPageIndex = this.PageCount - 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
}
}
}
return handled || base.OnBubbleEvent(source, e);
}
以上OnBubbleEvent方法主要應用於複合控制元件中,採用冒泡形式處理子控制元件事件,後面介紹複合控制元件冒泡處理事件機制時再詳細講解此方法。另外,在控制元件的翻頁欄中預先放置了四個翻頁功能的按鈕,分別表示“首頁”、“上一頁”、“下一頁”、“末頁”,並設定它們的屬性CommandName都為“Page”,CommandArgument分別為“ButtonFirst”,“ButtonPrev”,“ButtonNext”,“ButtonLast”。
這樣就可以根據按鈕的命令和引數確定執行什麼樣的邏輯。這裡僅拿按鈕“下一頁”(Command="Page"&&CommandArgument="ButtonNext")為例解釋一下程式碼邏輯:
/// <summary>
/// 獲得本書更多內容,請看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
if (lb.CommandArgument == "ButtonNext")
{
GridPageChangedEventArgs ee = new GridPageChangedEventArgs();
if (this.CurrentPageIndex < this.PageCount - 1)
{
this.CurrentPageIndex += 1;
ee.CurrentPageIndex = this.CurrentPageIndex;
ee.PageCount = this.PageCount;
ee.PageSize = this.PageSize;
this.OnPageIndexChanged(ee);
}
handled = true;
}
程式碼中首先定義一個頁引數類物件,然後通過條件語句判斷當前是否是最後一頁,如果不是最後一頁,則從主控制元件上讀取當前頁資訊(當前頁、頁數、頁記錄數),並賦值給GridPageChangedEventArgs物件,然後以頁引數物件作為引數呼叫this.OnPageIndexChanged方法引發事件。另外,注意LinkButton的CommandName和CommandArgument屬性的組合用法。
最後,看一下OnPageIndexChanged方法程式碼:
/// <summary>
/// 獲得本書更多內容,請看:
/// http://blog.csdn.net/ChengKing/archive/2008/08/18/2792440.aspx
/// </summary>
protected new void OnPageIndexChanged(GridPageChangedEventArgs e)
{
GridPageChangedEventHandler handler1 = (GridPageChangedEventHandler)base. Events[Grid.EventPageIndexChanged];
if (handler1 != null)
{
handler1(this, e);
}
}
此方法的功能是從base.Events物件中取出以Grid.EventPageIndexChanged為Key的事件引用控制程式碼,假如控制程式碼不為null(開發人員註冊了翻頁事件),則引發事件方法體。
最後,說明非常重要的一點:如果自定義了事件引數類,並要求在開發人員註冊的事件體中自動顯示改變引數物件的型別,如:
protected void Grid1_PageIndexChanged(object source, GridPageChangedEventArgs e)
{
//… …
}
中的第二個引數顯示為GridPageChangedEventArgs型別,而不是預設的EventArgs型別,我們也必須定義自己的委託(如本例定義了委託GridPageChangedEventHandler);而預設的委託EventHandler對應的引數型別為基類System.EventArgs,即如果這裡將預設的委託EventHandler和GridPageChangedEventArgs類一起使用的話,則生成以下的程式碼語句:
protected void Grid1_PageIndexChanged(object source, EventArgs e)
{
//… …
}
可以看出引數變為EventArgs型別了。這樣就不能利用我們自己定義的GridPage ChangedEventArgs類中的資料了。雖然可以使用(GridPageChangedEventArgs)EventArgs的方式轉換一下也可以取得GridPageChangedEventArgs物件中的資料,但據筆者瞭解還沒有開發人員這麼用,因為沒有人知道還有個GridPageChangedEventArgs類。
這一節主要講解如何定製自己的引數類和委託,並以Grid的分頁功能為例演示其在實際開發中的應用。下一節開始講解複合控制元件的事件機制。