VB.NET學習筆記:事件處理及自定義事件
測試環境:windows 7和Microsoft Visual Studio 2015
程式設計必然要知道事件,比如單擊按鈕事件,然後編寫當事件發生時的處理程式碼,VB.NET是怎樣進行事件處理的呢?可否自定義事件呢?因擴充套件控制元件,使其列頭增加全選全不選複選框並具備分頁功能需要用到自定義事件,詳見博文《VB.NET學習筆記:WinForm自定義DataGridView分頁組合控制元件》、《VB.NET學習筆記:自定義控制元件之擴充套件DataGridViewColumnHeaderCell類增加CheckBox全選複選框》,所以拜讀了多篇相關事件的博文,結合自己在程式設計中的使用情況,談談我對事件的一些認識。
一、事件處理——把事件與處理事件的方法關聯在一起
新建一個Windows窗體應用程式專案,在窗體里拉入一個Button控制元件,將其Name屬性值修改為BtnEvent1,Text屬性值修改為“按鈕1”,然後雙擊按鈕,即可自動生成如下按鈕單擊事件處理程式碼:
Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
End Sub
當然也可以在程式碼窗口裡,選擇相應的專案名稱,及其下相應的控制元件,就可以選擇對應的事件,之後也會自動生成事件處理程式碼,如下圖所示操作。
程式碼比VB6.0裡的事件處理程式碼多了一些內容:
1、Handles BtnEvent1.Click:表明這個方法應處理BtnEvent1中的Click事件,Handles的目的就是把這個方法與Button類中的Click事件關聯起來。
2、(sender As Object, e As EventArgs) :兩個引數sender和e。這是控制元件類定義的引數,如果引數不對將引發錯誤。
引數sender和e到底是個啥呢?我們來測試一下,在事件處理程式碼中新增如下程式碼:
Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
MessageBox.Show(sender.ToString)
MessageBox.Show(e.ToString)
End Sub
除錯程式碼,測試結果如圖所示:
經在csdn中查詢得知:sender引數表示傳送者,此處表示按鈕。e引數是MouseEventArgs類,繼承自EventArgs類,就是將滑鼠的動作資訊封裝在這個類的物件e中。這個物件描述了滑鼠點選情況,比如在按鈕的那個位置點下了滑鼠。我自己也不是很懂,在這裡做個記號吧!
下面程式碼修改了事件處理的方法名:
Private Sub Btn1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
MessageBox.Show(sender.ToString)
MessageBox.Show(e.ToString)
End Sub
BtnEvent1_Click修改成了Btn1_Click,如果在VB6.0是沒有辦法識別這個事件的,單擊“按鈕1”當然也不會執行此程式碼。但在vb.net中不影響事件的執行,說明在vb.net中,事件不再依賴方法名。
我們在窗體中再拉入一個Button控制元件,將其Name屬性值修改為BtnEvent2,Text屬性值修改為“按鈕2”,然後新增如下按鈕單擊事件處理程式碼:
Private Sub BtnEvent2_Click(sender As Object, e As EventArgs) Handles BtnEvent2.Click
MessageBox.Show(sender.ToString)
MessageBox.Show(e.ToString)
End Sub
BtnEvent2_Click方法裡程式碼與BtnEvent1_Click方法裡的程式碼一模一樣,引數也一樣,只是關聯的事件不同而已,我們可以這兩個方法合成一個方法,然後關聯到2個事件上:
Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click, BtnEvent2.Click
MessageBox.Show(sender.ToString)
MessageBox.Show(e.ToString)
End Sub
Handles關鍵字可以關聯多個事件,用逗號隔開每個事件,每個事件都會導致方法執行。在合併方法時要求每個事件的引數個數及型別必須相同。
一個方法可以關聯多個事件,當然一個事件也是可以引發多個方法。程式碼如下:
Private Sub BtnEvent1_Click1(sender As Object, e As EventArgs) Handles BtnEvent1.Click
Me.Label1.Text = Me.Label1.Text & Environment.NewLine & "方法:BtnEvent1_Click1"
End Sub
Private Sub BtnEvent1_Click2(sender As Object, e As EventArgs) Handles BtnEvent1.Click
Me.Label1.Text = Me.Label1.Text & Environment.NewLine & "方法:BtnEvent1_Click2"
End Sub
Private Sub BtnEvent1_Click3(sender As Object, e As EventArgs) Handles BtnEvent1.Click
Me.Label1.Text = Me.Label1.Text & Environment.NewLine & "方法:BtnEvent1_Click3"
End Sub
測試效果如下圖所示:
可見,單擊“按鈕1”時,每個關聯的方法都被執行了。
除了用Handles關鍵字關聯事件與事件處理方法外,還可以用AddHandler關鍵字,請看下面程式碼:
Private Sub BtnEvent1_Click(sender As Object, e As EventArgs) Handles BtnEvent1.Click
RemoveHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
Me.TextBox1.Text = sender.ToString
AddHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
End Sub
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
MessageBox.Show("你修改了文字!")
End Sub
不管是在窗體修改或是程式設計修改TextBox控制元件文字都會引發TextChanged事件,上面程式碼可以先暫停引發該事件,使用到RemoveHandler關鍵字來把事件跟處理事件的方法分離。
RemoveHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
之後要記得用AddHandler關鍵字重新將事件TextBox1.TextChanged跟TextBox1_TextChanged方法關聯,要不然就真的再不也會引發TextBox控制元件的TextChanged事件了。
AddHandler TextBox1.TextChanged, AddressOf TextBox1_TextChanged
學習小結:
(1)、把事件處理方法和事件關聯的方式有兩種:
A、Handles關鍵字:只要關聯某方法後,就會一直關聯。我們稱其為靜態方式。
B、AddHandler關鍵字:AddHandler可以在程式執行時動態的新增處理事件的方法,還可以用RemoveHanlder動態地刪除處理事件的方法。
(2)、一個事件可以關聯多個方法,一個方法也可以關聯多個事件。
如果事件關聯的方法不止一個時,引發事件就會執行全部關聯的方法,我們無法干涉和終止,而且也不能預測執行方法的順序。
二、自定義事件
在vb.net中事件可以看成為一種特殊的委託,也就是說可以將sub過程委託給事件,當觸發事件時就會執行委託給該事件的sub。
1、使用自定義事件的步驟
(1)、宣告事件
事件由委託實現,所以應事先宣告一個事件的委託。
Public Delegate Sub CustomEventHandler(ByVal str As String) '宣告一個事件的委託
Public Event CustomEvent As CustomEventHandler '宣告一個自定義事件CustomEvent
也可以用簡寫,即上述宣告等效於:
Public Event CustomEvent(ByVal str As String) '宣告一個自定義事件CustomEvent
注意:宣告事件可以帶引數,但不能具有返回值、可選引數、ParamArray引數。也就是說不能將function函式委託給事件。
(2)、觸發事件
使用RaiseEvent關鍵字來引發事件。
注意:觸發事件時傳入的引數必須與宣告事件時的引數型別以及個數都要相同。
Public Sub RaiseCustomEvent(ByVal str As String) '定義觸發事件的方法
RaiseEvent CustomEvent(str) '觸發事件
End Sub
(3)、編寫處理事件的Sub方法
我這裡編寫的方法名為talk,帶一個引數,特別注意的是傳入方法的引數必須與宣告事件時的引數型別以及個數都要相同。
Public Sub talk(ByVal str As String)
Me.TextBox1.Text = str
End Sub
(4)、把事件和處理事件的方法關聯在一起
可以使用Handles關鍵字或AddHandler關鍵字,但使用Handles關鍵字一定要配合使用WithEvents關鍵字來宣告事件所在的物件,如果不帶WithEvents關鍵字不會報錯,但若這個物件真的有事件,也不會引發;當然如果物件本身沒有宣告的事件,如果強行用WithEvents將會引發語法錯誤。所以WithEvents和Handles必須成對出現。
2、WithEvents和Handles關聯事件方法
類程式碼:
Public Class ClsCustomEvent '自定義類
'Public Delegate Sub CustomEventHandler(ByVal str As String) '宣告一個事件的委託
'Public Event CustomEvent As CustomEventHandler '宣告一個自定義事件CustomEvent
Public Event CustomEvent(ByVal str As String) '宣告一個自定義事件CustomEvent
Public Sub RaiseCustomEvent(ByVal str As String) '定義觸發事件的方法
RaiseEvent CustomEvent(str) '觸發事件
End Sub
End Class
窗體程式碼:
Public Class Form1
Public WithEvents Custom As New ClsCustomEvent() '宣告事件物件
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Custom.RaiseCustomEvent("Hello world") '執行ClsCustomEvent類中觸發CustomEvent事件的方法
End Sub
Public Sub talk(ByVal str As String) Handles Custom.CustomEvent '將talk方法委託給ClsCustomEvent類的CustomEvent事件
Me.TextBox1.Text = str
End Sub
End Class
3、AddHandler關聯事件方法
事件所在的類程式碼:
Public Class ClsCustomEvent '自定義類
'Public Delegate Sub CustomEventHandler(ByVal str As String) '宣告一個事件的委託
'Public Event CustomEvent As CustomEventHandler '宣告一個自定義事件CustomEvent
Public Event CustomEvent(ByVal str As String) '宣告一個自定義事件CustomEvent
Public Sub RaiseCustomEvent(ByVal str As String) '定義觸發事件的方法
RaiseEvent CustomEvent(str) '觸發事件
End Sub
End Class
窗體程式碼:
Public Class Form1
Public Custom As New ClsCustomEvent() '宣告事件物件
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler Custom.CustomEvent, AddressOf talk '將talk方法委託給ClsCustomEvent類的CustomEvent事件
Custom.RaiseCustomEvent("Hello world") '執行ClsCustomEvent類中觸發CustomEvent事件的方法
End Sub
Public Sub talk(ByVal str As String)
Me.TextBox1.Text = str
End Sub
End Class
注意: AddHandler與RemoveHandler最好能成對出現,如果不將事件處理程式與事件分離(RemoveHanlder),物件將保留在記憶體中,即使上面Custom不再指向物件,每個事件仍有物件的一個引用,容易出現記憶體洩露。而WithEvents會自動處理這些細節。
本文借鑑與參考了以下博文:
VB.net學習筆記(七)物件事件的定製
vb.net中的委託與事件
學習過程得到網友uruseibest的幫助,表示感謝!