一、What's 事件
類或對象可以通過事件向其他類或對象通知發生的相關事情。發送(或引發)事件的類稱為“發行者”,接收(或處理)事件的類稱為“訂戶”(訂閱者)。
在典型的 C# Windows 表單或 Web 應用程式中,可訂閱由按鈕和列表框等控制項引發的事件。可以使用整合式開發環境 (IDE) 來瀏覽控制項發布的事件,並選擇想要處理的事件。 IDE 將自動添加空白事件處理常式方法和訂閱該事件的代碼。
二、事件的屬性匯總
發行者確定何時引發事件;訂戶確定對事件作出的響應。
一個事件可以有多個訂戶。訂戶可以處理來自多個發行者的多個事件。
沒有訂戶的事件永遠也不會引發。
事件通常用於表示使用者操作,例如單擊按鈕或圖形化使用者介面中的菜單選項。
當事件具有多個訂戶時,引發該事件時預設會同步呼叫事件處理常式。
事件基於 EventHandler 委託和 EventArgs 基類。
三、事件的訂閱和取消
如果您想編寫引發事件時調用的自訂代碼,則可以訂閱由其他類發布的事件。例如,可以訂閱某個按鈕的 click 事件,以使應用程式在使用者單擊該按鈕時執行一些有用的操作。
1.用 IDE 訂閱事件
圖3-1-1 建立一個 WinForm 項目
圖3-1-2 由圖1雙擊時所自動建立的代碼
圖3-1-3 除了圖2,在 InitializeComponent 方法也自動增加了這行代碼
2.以編程方式訂閱事件
假設現在是一個新的 WinForm 程式,我們通過手動的方式自己建立事件。在 InitializeComponent 方法下面輸入 this.Load +=,然後會出現提示,這時我們按下“Tab 鍵”,
圖3-2-1
會發現也會自動建立事件處理常式,效果跟上一節的直接雙擊空白處建立的代碼可以說是一致的,代碼如下:
public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Load += Form1_Load; } private void Form1_Load(object sender, EventArgs e) { throw new NotImplementedException(); } }
這次,我們直接採用 lambda 方式完成事件的註冊:點擊空白處顯示滑鼠點擊時的座標。
public partial class Form1 : Form { public Form1() { InitializeComponent(); //this.Load += Form1_Load; //點擊事件(lambda 方式建立) this.Click += (s, e) => { MessageBox.Show($"{((MouseEventArgs)e).Location}"); }; } private void Form1_Load(object sender, EventArgs e) { throw new NotImplementedException(); } }
【備忘】內容涉及 $: (C# 6) 的文法,vs2015 以上才支援。$"{msg}" 相當於 string.Format("{0}", msg), msg 指變數。
3.用匿名方法訂閱事件
public Form1() { InitializeComponent(); //this.Load += Form1_Load; //點擊事件(lambda 方式建立) //this.Click += (s, e) => //{ // MessageBox.Show($"{((MouseEventArgs)e).Location}"); //}; //使用匿名方法建立事件 this.Click += delegate (object sender, EventArgs e) { var mouseEventArgs = (MouseEventArgs)e; var mouseLocation = mouseEventArgs.Location; MessageBox.Show($"X: {mouseLocation.X}, Y: {mouseLocation.Y}"); }; }
【注意】如果使用匿名函數訂閱事件,事件的取消訂閱過程將比較麻煩。這種情況下若要取消訂閱,必須返回到該事件的訂閱代碼,將該匿名方法儲存在委託變數中,然後將此委託添加到該事件中。一般來說,如果必須在後面的代碼中取消訂閱某個事件,則建議您不要使用匿名函數訂閱此事件。
4.取消訂閱
要防止在引發事件時呼叫事件處理常式,請取消訂閱該事件。要防止資源流失,應在釋放訂戶對象之前取消訂閱事件。在取消訂閱事件之前,在發布對象中作為該事件的基礎的多路廣播委託會引用封裝了訂戶的事件處理常式的委託。只要發布對象保持該引用,記憶體回收功能就不會刪除訂戶對象。使用減法賦值運算子 (-=) 取消訂閱事件。
this.Load -= Form1_Load; //使用減法賦值運算子 (-=) 取消訂閱事件
【備忘】所有訂戶都取消訂閱事件後,發行者類中的事件執行個體將設定為 null。