Custom Event 入門
Custom Eventに挑戦
存在は知っていたけど、実はやったことなかった。
『VBA Developer's Handbook Second Edition』の説明が実にわかりやすかったので、ちょっとやってみようと思った。
まあ、単なる覚書です。
手順
今回は、三つのクラスモジュールを用いる。
Aho
クラス
アホを表すクラス(笑)。Ahoes
クラスAho
クラスのインスタンスの集合体を表すクラス。このクラスにイベントを装備する。- AhoTestクラス
Ahoes
オブジェクトでイベントが起こったときに、そのイベント発生を検知して処理を行うクラス。
うーむ、わかりにくい。
もう少し詳しくやろうとしていることを述べる。
Ahoes
オブジェクトにAho
オブジェクトを追加するAdd
メソッドを持たせる。
Ahoes
オブジェクトでAdd
メソッドが実行されると、Added
イベントが起こるようにする。
AhoTest
オブジェクトにAdded
イベントを検知したときに実行されるイベントプロシージャを置く。
これで、AhoTest
オブジェクト内の処理でAhoes
クラスのAdd
メソッドを実行したときに、イベントプロシージャが実行されることになる。
うーむ、やっぱりわかりにくい。
Ahoクラスを作る
まずは、Aho
クラスを作る。
クラスモジュール Aho
Option Explicit Private name_ As String Public Property Get Name() As String Name = name_ End Property Public Sub init(ByVal name__ As String) name_ = name__ End Sub Public Sub introduceMyself() Dim msg As String msg = "アホ、アホ、アホの" & Me.Name & "~♪" Call MsgBox(msg) End Sub
init
メソッドで名前を設定する。
Name
プロパティを持ち、introduceMyself
メソッドで素敵な自己紹介をするクラス。
Ahoesクラスを作る
次に、先ほど作成したAho
クラスのインスタンスを大量に保持することのできるAhoes
クラスを作成する。
クラスモジュール Ahoes
Option Explicit 'Attribute Item.VB_UserMemId = 0 設定済み' 'Attribute NewEnum.VB_UserMemId = -4 設定済み' 'Event declarations' Public Event Added(ByVal AhoName As String) '……(1)' 'Module level variables' Private ahoes_ As Collection 'Properties' Public Property Get Count() As Long Count = ahoes_.Count End Property 'Constructor' Private Sub Class_Initialize() Set ahoes_ = New Collection End Sub 'Methods' Public Function Item(ByVal Index As Variant) As Aho Set Item = ahoes_.Item(Index) End Function Public Sub Add(ByVal AhoName As String, _ Optional ByVal Key As Variant, _ Optional ByVal Before As Variant, _ Optional ByVal After As Variant) Dim item_ As New Aho Call item_.init(AhoName) 'Fire event' RaiseEvent Added(AhoName) '……(2)' Call ahoes_.Add(item_, Key, Before, After) End Sub Public Sub Remove(ByVal Index As Variant) Call ahoes_.Remove(Index) End Sub Public Function NewEnum() As IUnknown Set NewEnum = ahoes_.[_NewEnum] End Function
組み込みのCollection
クラスをラップしたクラスにしている。
Item
メソッドをデフォルトメンバにしたり、NewEnum
メソッドを実装して内部Collection
のahoes_
の要素(Aho
オブジェクト)をFor Each ~ Next
で回せるようにしたりしているのだが、今回は関係ないのでパス。
ポイントは二つ。
まず宣言セクションにある、(1)の
Public Event Added(ByVal AhoName As String)
というやつ。
これがイベント宣言。「このクラスはこんなイベントを持っています」というもの。引数を持たせることができるので、イベントを検知して実行するプロシージャに引数を渡すことができる。
もう一つが、イベントを発生させたいAdd
イベント内にある(2)の
RaiseEvent Added(AhoName)
というやつ。
もう読んで字の如しで、この行が実行されると「イベント発生!」ということになる。『VBA Developer's Handbook Second Edition』では「Fire event」と説明されていた。
まさに、「発火」というイメージ。
AhoTestクラスを作る
さて、ここまででAdded
イベントが起こるようにすることはできた。
Ahoes
クラスのインスタンスを作成して、Add
メソッドを実行すれば、めでたくAdded
イベントは起こる。
しかし、このままではただイベントが起こるだけ。人知れずイベントが発生して終了し、見かけ上は何も起こらない。
イベントが起こったことを検知して、何らかの処理を行わねばならぬ。
そのために、イベントに対応して処理を行うクラスを作る。
それが、AhoTest
クラスである。
クラスモジュール AhoTest
Option Explicit Private WithEvents AhoesObject As Ahoes '……(3)' Public Property Get Ahoes() As Ahoes Set Ahoes = AhoesObject End Property Private Sub Class_Initialize() Set AhoesObject = New Ahoes End Sub Private Sub AhoesObject_Added( _ ByVal AhoName As String) '……(4)' Dim msg As String msg = "アホの" & AhoName & vbCrLf msg = msg & msg & msg & msg msg = msg & "アホの" & AhoName & "~~~~♪" & vbCrLf msg = msg & "おれぁ、そういう男よ。" Debug.Print msg End Sub
ポイントは二つ。
まず、宣言セクションにある(3)の
Private WithEvents AhoesObject As Ahoes
こいつ。
Ahoes
クラスのインスタンスを保持するためのモジュールレベル変数なんだが、WithEvents
というキーワードが付いている。
こうすることで、AhoesObject
は、単なるAhoes
クラスのインスタンスではなく、Ahoes
オブジェクトで起こったイベントを検知することのできるちょっと特別なインスタンスになるのだ!
そして、(4)の
Private Sub AhoesObject_Added( _ ByVal AhoName As String) Dim msg As String msg = "アホの" & AhoName & vbCrLf msg = msg & msg & msg & msg msg = msg & "アホの" & AhoName & "~~~~♪" & vbCrLf msg = msg & "おれぁ、そういう男よ。" Debug.Print msg End Sub
こいつが、Added
イベントが検知されたときに実行されるプロシージャ。
AhoesObject
という変数にぶちこまれたAhoes
オブジェクトでAdd
メソッドが実行されると、こいつが実行されるのだ。
使ってみる
次のコードで使ってみる。
リスト1
Private Sub test02() Dim ahoAho As AhoTest Set ahoAho = New AhoTest Call ahoAho.Ahoes.Add("坂田") End Sub
AhoTest
クラスのインスタンスを作成し、AhoTest
クラスのAhoes
プロパティでAhoes
オブジェクトを取得してAdd
メソッドを実行する。
AhoTest
クラス経由で[Ahoes].Add
メソッドを実行することによって、イベントを検知してAhoesObject_Added
を実行させようという目論見。
うーむ、やっぱりわかりにくい。
よって、単純に実行結果をお目にかけるよりも、ステップ実行の様子をお目にかけた方がよかろう。
こんな感じ。
おわりに
標準モジュールでWithEvents
キーワードを用いた変数宣言ができたら、もうちょっと簡単に説明ができるのだが、残念ながら標準モジュールでは不可。
『VBA Developer's Handbook Second Edition』には、
You can only use WithEvents with variables declared at the module level and only within class modules. The reason for this is that VBA uses COM to supply your project with events, and COM requires that both event generators and event listeners be objects, thus the need for class modules.
と書いてある。
尤も、上記引用文中の"class modules"にはシートモジュールも含まれるので、今回の例の場合、別にAhoTest
クラスなんぞ作らずとも、シートモジュールを使えば同じことができます。
リスト2 シートモジュールに記述
Option Explicit Private WithEvents AhoesObject As Ahoes Private Sub test() Set AhoesObject = New Ahoes Call AhoesObject.Add("坂田") End Sub Private Sub AhoesObject_Added(ByVal AhoName As String) Dim msg As String msg = "アホの" & AhoName & vbCrLf msg = msg & msg & msg & msg msg = msg & "アホの" & AhoName & "~~~~♪" & vbCrLf msg = msg & "おれぁ、そういう男よ。" Debug.Print msg End Sub
それにしても、わかりにくいなあ。
もっとわかりやすい説明が思いついたら、書き直そう。