今回はクラスモジュールに、自作のEventを実装し、シートモジュールでクラスのイベントを受け取る処理をやってみる。
まずはEventを使用しないクラスサンプル
クラスモジュールを挿入し、以下のコードを貼り付け。
※サンプルなのでクラスモジュールのオブジェクト名はClass1のままとする。
Private num As Long Property Get Value() As Long Value = num End Property Property Let Value(x As Long) num = x End Property
次にSheet1モジュールに以下を貼り付け
Private C As Class1 Sub Init() Set C = New Class1 End Sub Sub Plus10() C.Value = C.Value + 10 Debug.Print C.Value End Sub
まずInitにカーソルを合わせてF5で実行すると、Sheet1モジュールの変数CがClass1のオブジェクトを保持する。
次にPlus10マクロをF5で実行すると、実行するたびにイミディエイトウインドウに数値が出力される。
まぁ、ここまでは今までやってきたクラスモジュールの扱いとなんら変わらない。
Eventを使ったクラスモジュール
さて、早速Class1モジュールの改造を始めよう。
Class1にイベントを実装するので、以下のように書き換える。
Private num As Long Public Event Change(v As Long) Property Get Value() As Long Value = num End Property Property Let Value(x As Long) num = x RaiseEvent Change(num) End Property
変わったのはまず以下の部分。
Public Event Change(v As Long)
これはイベントの宣言である。変数などと同じようにイベントも宣言が必要だ。
そしてValueプロパティのLet部に書いた次のコード。
RaiseEvent Change(num)
これが、実際にイベントを発動させるコードである。
つまりこれで、Class1はValueが変更された際にChangeというイベントを発動するようになったわけだ。
次に、Sheet1モジュールのコードを、イベントを受け取れるように書き換える。
Private WithEvents C As Class1 Sub Init() Set C = New Class1 End Sub Sub Plus10() C.Value = C.Value + 10 End Sub Private Sub C_Change(v As Long) Debug.Print v End Sub
まず変数Cの宣言部に付いたWithEventsキーワードに注目。
Private WithEvents C As Class1
これで、変数Cはイベントを受信可能なオブジェクトになった。
すると、コード上部のオブジェクト選択ボックスに、変数Cが現れるようになる。
ここでCを選択すると、自動的に以下のコードが挿入される。
Private Sub C_Change(v As Long) End Sub
この時、仮引数のvにはクラスから送信されたnumが入る。
こんなイメージ。
最終的には受け取ったパラメータをDebug.Printで表示させている。
Private Sub C_Change(v As Long) Debug.Print v End Sub
また、Plus10プロシージャに書いたDebug.Printを削除する。
さて、これでまたInitを実行すれば準備完了。
Plus10を実行するたびにイベントが発動され、イミディエイトウインドウに数値が出力される。
以上がイベントの実装方法である。
自作Eventはどんな時に使うのか。
さきほどこんなマクロを作ってみた。
これはADOのRecordSetの動きをわかりやすく説明するために擬似的に動作を再現しようと作成中のものである。
※完成ではないが、今回のイベントの説明にあたって支障はない。
ちなみに赤いカーソルは▲を書式設定でマイナス90度回転させたもの。
これもテクニックとして使えるので覚えておくと良いと思う。
さて、カーソルを動かすために、内部ではクラスモジュールCursorを使用している。
といってもシートのカーソル書き換えはシートモジュールが行っており、Cursorモジュールの役割はあくまで現在値の管理と変更された際のイベント発動である。
以下がクラスモジュールCursorのコード。
イベント宣言に注目。パラメーターとしてcurrent(現在値)とprevious(ひとつ前の値)を設定している。
Private innerValue As Long Public Event Change(current As Long, previous As Long) Sub Init(n As Long) innerValue = n End Sub Property Get Value() As Long Value = innerValue End Property Property Let Value(n As Long) Dim pv As Long: pv = innerValue innerValue = n RaiseEvent Change(innerValue, pv) End Property Sub MoveNext() Value = innerValue + 1 End Sub Sub MovePrevious() Value = innerValue - 1 End Sub
またMoveNext、MovePreviousメソッドではinnerValueを直接変更せず、Valueプロパティ経由で変更するようにしている。そのため初期設定を行うInitメソッドを別にすれば、値の変更は必ずValueプロパティでイベントを発動させることになる。
またValueプロパティ(Let)の内部では、実際にinnerValueを変更する前に変数pvに控えておき、イベントでは変更後と変更前を両方パラメーターとして送信するようにした。
次にこのイベントを受け取るSheet1モジュールのコード。
Private WithEvents Cur As Cursor Sub 初期化_Clicked() Set Cur = New Cursor Cur.Init 3 Range("B3").Value = "▲" End Sub Sub MoveNext_Clicked() Cur.MoveNext End Sub Sub MovePrevious_Clicked() Cur.MovePrevious End Sub Private Sub Cur_Change(current As Long, previous As Long) Range("B" & previous).ClearContents Range("B" & current).Value = "▲" End Sub
初期化ボタンをクリックした際にCursorクラスのインスタンスが生成され、Curにセットされる。
そしてCurの値(行を表す)が3に初期化され、実際にB3セルにカーソルを書き込む処理も行っている。
MoveNextボタンやMovePreviousボタンをクリックした際は、Cur変数が保持しているCursorオブジェクトのメソッドによりValueプロパティが変化し、イベントが発動される。
するとSheet1プロシージャに予め作成しておいたCur_Changeプロシージャに制御が移り、前のカーソルを削除と新しいカーソルの描画が行われる。
MoveNextをクリックした際の制御の流れはこのようになる。
Eventを利用すると何がどう便利なのか
たとえばEventを利用せずにカーソルの描画を行う場合、以下の4パターンが考えられる。
- Sheet1のMoveNext_Clickedの中で処理する。
- Sheet1のMoveNext_Clickedから、カーソル描画用のプロシージャを呼び出す。
- CursorのMoveNextメソッドで処理する。
- CursorのValueプロパティで処理する。
1はまあ、却下である。なぜならMovePreviousでも同じような処理をするのに、個別のプロシージャに書いたら重複ができてしまいメンテ時に困るから。
2は、1よりマシだけど、各ボタンマクロがCursorの操作に加えて共通プロシージャの呼び出しまで実施しなければならない。
3と4はCursorクラスがSheet1の実装に大きく依存してしまい、汎用性がなくなる。
Eventを利用した場合はどうなるかというと、
- 各ボタンマクロは、Cursorを操作するだけで良くなる。
- カーソルの描画は、Cursorの変化というイベント処理で一元化できる。
- Cursorは変化をイベントとして発行するだけなので、Sheet1の実装には依存しない。
このように、先ほどのデメリットが払拭されている。
まとめ
さて、今回はEventを実装する方法とそのメリットを簡単に説明した。といっても私自身、Eventを実装したのが今回初めてで、機能としては知っていたものの何がどう便利なのかわかっていなかった。だから皆さんにもうまく伝えられたかどうか分からない。
まだEvent機能については勉強しはじめたところなので、また「こういう場合に使うと便利」ってのが見つかったら都度紹介しようと思う。
参考書籍
既に絶版しているし、英語だけれど自作イベント処理等に言及されている素晴らしい書籍。
- 作者: Ken Getz,Mike Gilbert
- 出版社/メーカー: Sybex
- 発売日: 2001/04/25
- メディア: ペーパーバック
- クリック: 18回
- この商品を含むブログ (1件) を見る
以下で詳しくレビューしているので興味のある方はどうぞ。
thom.hateblo.jp