想了解一下VBA中自定義類和事件,以及注冊事件處理程序的方法。
折騰了大半天,覺得這樣的方式實在稱不上“注冊”,所以加一個“偽”字。純粹是瞎試,原理也還沒有摸透。先留着,有時間再接着摸。
做以下嘗試:
1、建一個自定義類(類模塊),類名:Qiqiu
該類提供一個Daqi的方法,每執行一次,x(記錄氣球的體積)的值+i,如果x的值大於max,則觸發自定義的Change事件。
為節省細節不使用屬性過程,變量直接用public
1 Public Event Change(q As qiqiu) 'Event關鍵字聲明事件,事件參數是Qiqiu類型。
'推模式還是拉模式?一直感覺有參數的都該是推模式,傻傻分不清楚啊。 2 Public x As Integer '記錄實際體積 3 Public max As Integer '記錄最大體積 4 Function Daqi(i As Integer) '模擬給Qiqiu打氣的情形 5 x = x + i 6 If x > max Then 7 RaiseEvent Change(Me) 'RaiseEvent關鍵字觸發事件。在事件觸發時,把自已的實例引用傳給訂閱者。 8 x = 0 9 End If 10 End Function
2、建三個簡單窗體MainFrm,UserFrm1,UserFrm2
MainFrm啟動后,點擊“彈出窗體按鈕”,UserFrm1和UserFrm2顯示出來
3、UserFrm1、UserFrm2需要關聯Qiqiu的Change事件(觀察氣球狀態的變化對事件做出響應),需要做一些准備
下面是UserFrm1中的代碼(為簡化案例,UserFrm2的代碼和UserFrm1完全相同,實際上可以完全是不同的響應)
注意第一行的WithEvents關鍵字的變量聲明,后面需要使用這個變量將方法(事件處理程序)關聯到事件。
1 Public WithEvents qiu As Qiqiu '關聯Qiqiu的事件的關鍵,維護一個Qiqiu的引用,既然有引用,本案的Change事件的參數就顯得很多余。 2 3 Private Sub qiu_Change(q As Qiqiu) '事件的響應程序
4 Me.TextBox1 = "氣球爆炸了,爆炸時體積是:" + CStr(q.x) 5 End Sub
4、MainFrm主窗體代碼:
1 Public q As Qiqiu 3 Private Sub UserForm_Initialize() 4 Set q = New Qiqiu '窗體初始化,初始化Qiqiu類的實例 5 q.max = 10 '將q的最大體積設定為10 6 End Sub 8 9 Private Sub btn_Click() '點擊按鈕“彈出窗體”執行的代碼,實例化UserFrm1和UserFrm2並顯示 11 Dim f1 As UserFrm1, f2 As UserFrm2 13 Set f1 = New UserFrm1 14 Set f2 = New UserFrm2 16 Set f1.qiu = q '第3中的WithEvents關鍵字聲名的變量在此處使用 17 Set f2.qiu = q '使f1.qiu,f2.qiu分別指向Qiqiu類的實例q(即:注冊) 19 f1.Show False 20 f2.Show False 22 End Sub 23 24 Private Sub btndq_Click() '點擊按鈕“打氣”執行的代碼 26 q.daqi (5) '調用q的打氣方法給Qiqiu打氣,每次打入氣體體積為5。當q.x大於q.max時觸發事件 28 End Sub
5、程序執行效果:(雖然實現了效果,但理解上感覺模模糊糊)
打氣三次時觸發事件(此時氣球的體積是15,超過了氣球的max體積10),事件關聯的處理程序提示,氣球爆炸,並獲取爆炸時的體積
6、小結:
VBA中,類的事件可能是很封閉的。不像C#事件開放了注冊和移除的接口,只要方法簽名相同,就可以很方便的指向事件的響應方法,根本不需要在訂閱者類的內部再聲明和發布者直接相關東西(變量引用),減小耦合度。
其實摸索了VBA的對象瀏覽器后,也可以找到類中事件的冰山一角,可以看到它的簽名。比如Worksheet的Change事件。
7、補充一點轉來的總結(我自己按想法修改了一些用詞):
主題對象(被觀察者、事件發布者)對客戶端(觀察者、訂閱者)一無所知
- 觀察者引用一個主題對象,對這個觀察者,它可將引用放置在 WithEvents 變量中來處理那些主題對象。發布者沒有訂閱者的信息。它向未知數目的聽眾進行廣播, 劇院中可能一個觀眾都沒有。
- 主題對象不會控制接收事件的觀察者的次序。(好像這點和C#有很大不同,C#事件注冊的順序可以決定事件的執行順序)
- 當對象引發事件時,其所有訂閱者都在引發事件的對象再次獲得控制之前處理該事件。
- 如果事件包含 ByRef 參數,則該參數可被任何處理事件的客戶程序改變。只有最后的客戶端進行的改變才對引發事件的對象可見,因為(如上所述),直到所有客戶端都處理該事件之前,引發事件的對象不會再度獲得控制。
為了將某個事件添加到一個類中,然后使用該事件,可以這樣做:
- 在定義類的類模塊聲明部分,用 Event 語句來聲明事件—該事件帶有希望它帶有的任何參數。事件總是 Public。 注意 事件不能有命名的參數、Optional可選的參數、或 ParamArray可變參數。事件沒有返回值。
- 在類模塊代碼中的合適地方,用 RaiseEvent 語句來引發事件,並提供所需要的參數。
- 在將要處理事件的模塊聲明部分,使用 WithEvents 關鍵字,添加該類類型的變量。它必須是一個模塊級的變量。
- 在代碼窗口左邊的下拉菜單上,選擇聲明為 WithEvents 的變量。
- 在代碼窗口右邊的下拉菜單上,選擇希望處理的事件。(可以為類聲明多個事件。)
- 使用所提供的參數,將代碼添加到事件過程中。