プログラム悪戦苦闘日記

はてなダイアリーからの移行(遺物)

Excel VBA コントロール処理を分散させる

 WindowsのルールというかMS仕様では、コントロールのイベント(メッセージ)はトップウィンドウが受け取る、というのがある。たとえばワークシート上のボタンコントロールがあったとして、こいつを押したときイベントを受け取るのはボタン自身ではなくてワークシートになる。これは何を意味するかというと、シート上のコントロールのイベント処理(イベントハンドラ)は、すべてワークシートモジュールに記述される。ちょっとしたマクロであればこの仕様は便利なのだが、ある程度の規模になると、処理が一箇所に集中しすぎて大変見にくくなる。プログラムがヘタれる。

 そこでイベントハンドラの記述を分散させよう。クラスモジュールを使うとこんな風にできる。たとえば、シート上にコマンドボタン HogeButton があったとしよう。クラスモジュールを新規作成して、モジュール名をHogeButtonEventHandlerという名前にする。そのモジュールに次のように記述する。

Private WithEvents handler As CommandButton

Property Let Handle(Byref obj As CommandButton)
    Set hander = obj
End Property

 WithEvents を使うと、イベントを捕獲できる変数が作れるらしい。3分で分かるかも知れないExcel VBAとか読むと、そう書いてあった。handlerをPrivateにしてあるのは、まぁいわゆるカプセル化ってやつだ。ぷろぱちーLetを使えば、3分でできるかもしれないExcel VBA本とかに書いてあるように、わざわざPublicにする必要がない。
 このhandler変数に、本物のHogeButtonを結びつける必要がある。一番良いタイミングは初期化のとき、すなわちWorkbook_Openだ。ThisWorkbookモジュールに次のように書く。

Private hogeHandler As HogeButtonEventHandler

Pirvate Sub Workbook_Open()
    hogeHandler.Handle = Worksheets("Sheet1").HogeButton
End Sub

 これで結び付け完了。あとは、HogeButtonEventHandlerモジュールにイベントハンドラを書きまくれば良い。

 ちなみに、クラスモジュール名に〜EventHandlerとか書いちまったが、このクラスモジュールはイベントハンドラではない。クラスモジュール内のhandler変数がイベントハンドラだ。まぁどうでもいいことかもしれないが、ときどき問題になったりするから、頭の片隅にでもいれておこう。

 これでいわゆるMVCモデルのCを分離できた。Mを標準モジュール、VをMicrosoft Excel Objectに書けばMVCの分離完了じゃん、と思ったのだが、Microsoft Excel Objectのモジュール内の関数には参照渡しができないらしい。だからVBAは(ry