【Access VBA】複数コントロールのイベント処理を集約する

VBA

複数のコントロールのイベント処理を1つのプロシージャに集約する方法です。

集約するというのは、例えば、フォームの中のテキストボックス全部について、GotFocusイベントが走る時に必ず所定の処理を走らせる、というものです。

これが何の役に立つかと言えば、例えば、「フォーカスがあるテキストボックスだけ背景色を変える」機能を実装する際、コーディングの量を減らすことができ、作業が楽になります。

方法ですが、WithEventsキーワードを使用してイベンドハンドラを自作することで実装します。

以下、具体的に解説しましょう。

集約しない場合どうなるか

例えば、Form1という名前のフォームにTextBox1、TextBox2という名前のテキストボックス2つがあるとします。

以下のような感じですね。

Form1

フォーカス時の背景色だけを変える処理を実装する場合、もし処理を集約しないと、VBAで以下のでコーディングを実装しないといけません。

'フォーカス取得時に背景色を変える
Private Sub TextBox1_GotFocus()
  TextBox1.BackColor = RGB(239, 242, 247)
End Sub

'フォーカス喪失時に背景色を戻す
Private Sub TextBox1_LostFocus()
  TextBox1.BackColor = vbWhite
End Sub

'フォーカス取得時に背景色を変える
Private Sub TextBox2_GotFocus()
  TextBox2.BackColor = RGB(239, 242, 247)
End Sub

'フォーカス喪失時に背景色を戻す
Private Sub TextBox2_LostFocus()
  TextBox2.BackColor = vbWhite
End Sub

1つのテキストボックスに対し、フォーカス取得時(GotFocusイベント)に背景色を変える処理、フォーカス喪失時(LostFocusイベント)に背景色を戻す処理という、合計2つの処理を各々のコントロールに対して実装する必要があります。

テキストボックスの数が多くなるとコーディングの量が増加して修正箇所も多くなり、効率が悪いです。

各イベント処理を1つのイベントハンドラにまとめる(WithEventsを使用する)

Access VBAでも、複数のイベント処理を1つのイベントハンドラにまとめることが可能です。

ちなみに、.NETだと、この手の処理を簡単に実装できるのですが、VBAは工夫が必要です。

ここで、用語が出てきたので整理します。

・イベント
フォーカス取得後・喪失後、起動後、終了時、キー入力時など、何らかの動作のこと。

・イベントハンドラ
イベントが発生した際に実行される処理のことです。
上でも挙げたTextBox1_GotFocus()とTextBox2_GotFocus()がそれになります。
どちらも「フォーカス取得後」です。

WithEventsキーワード

WithEventsキーワードに関しては以下のMicrosoftのドキュメントに解説があります。
Dimステートメントの解説ですがPrivateでもPublicでも同じです。

Dim ステートメント (VBA)

イベント処理の集約をするには、イベントハンドラを新たに実装しなければいけませんが、このWithEventsを使うのがポイントです。

オブジェクトの宣言では以下のように書きます。

Private WithEvents txtevent As TextBox

注意点としては、WithEventsキーワードを使用したオブジェクトは、配列にはできません。
1つのオブジェクトに1つのコントールが紐づきますので、作り方を工夫する必要があります(今回は複数のテキストボックスのイベントを1つのイベントハンドラに記述するので)。

具体的な実装方法は以下の通りです。

全体像と概念図

まず、Form1とは別のクラスを作成し、その中にイベントハンドラを記述します。
それを仮にclsEventsクラスとしましょう。

そのクラスを、Form1の中でオブジェクト配列として使用します。
UMLのクラス図で分かりやすく図示してみました(記法が間違っていたらごめんなさい)。

Form1とclsEventsのクラス図

こうすることで、複数のコントロールのイベント処理を1つのイベントハンドラに実装できます。

具体的な実装方法

まず、clsEventsクラスを作成します。

clsEventsクラスの作成

clsEvents

clsEventsクラスの中身は以下の通りです。

Option Compare Database
Option Explicit

Private WithEvents txtevents As TextBox

'TextBoxをセットするプロパティ
Public Property Let setTextBox(ByVal tb As TextBox)
    Set txtevents = tb
    txtevents.OnGotFocus = "[EVENT PROCEDURE]"
    txtevents.OnLostFocus = "[EVENT PROCEDURE]"
End Property

'フォーカス取得後処理
Private Sub txtevents_GotFocus()
    txtevents.BackColor = RGB(239, 242, 247)
End Sub

'フォーカス喪失後処理
Private Sub txtevents_LostFocus()
    txtevents.BackColor = vbWhite
End Sub

Private Sub Class_Terminate()
    Set txtevents = Nothing
End Sub

イベントに応答するオブジェクトをtxteventsとして宣言しています。
また、イベントハンドラもこのクラス(clsEvents)の中に書きます。
GotFocusでフォーカス時の背景色を変え、LostFocusでフォーカス喪失時に背景色を白に戻します。

注意点ですが、OnGotFocusプロパティとOnLostFocusプロパティに、文字列”[EVENT PROCEDURE]”をセットする必要があります
(イベントプロシージャとして実行する場合はこれがないと動作しません。MicrosoftのVBAリファレンスにも書いてありますので、参考までにどうぞ)。

補足ですが、WithEventsでオブジェクトを宣言すると、下の画像のように画面からイベントが選択できるようになります。

Form1

Form1のVisual Basicコードは以下の通り。

Option Compare Database
Option Explicit

Private events() As New clsEvents

'起動時処理
Private Sub Form_Load()
    Dim c As Control
    
    'TextBoxをセット
    ReDim Preserve events(0)
    For Each c In Me.Controls
        Select Case TypeName(c)
            Case "TextBox"
                ReDim Preserve events(UBound(events) + 1)
                events(UBound(events)).setTextBox = c
        End Select
    Next
    
End Sub

'終了時処理
Private Sub Form_Close()
    Erase events
End Sub

clsEventsクラスを、eventsオブジェクトの配列として宣言します。
フォーム起動時に、型が”TextBox”のコントロールだけをevents配列にセットしていきます。

フォーム終了時には配列を消去してやります。

これで、必要なコードは書けました。
フォームを表示させてみると、フォーカスがあるテキストボックスだけ背景色が設定され、外れると白に戻るはずです。

完成後のForm1のイメージ

イベントハンドラが2つある時はどうなる?

clsEventsとForm1の両方に同じイベントを記述すると、どうなるでしょうか?
ちょっと、試してみました。

Form1に、TextBox01のGotFocusイベントを追加します。
(メッセージを出して、背景を薄い緑色に変える処理)

'Form1のGotFocusイベント
Private Sub TextBox01_GotFocus()
    MsgBox "背景を緑にする処理"
    Me.TextBox01.BackColor = RGB(226, 240, 217)
End Sub

Form1を立ち上げると、以下のようになりました。
Form1側のイベント処理もきちんと実行されているようです。

「OK」を押すと、こうなります。
clsEvents側のイベント処理も実行されているようです。

つまり、①フォーム側のイベント処理 → ②追加したイベントハンドラ側の処理、という順序になっているようですね。

 

以上です。
終わり。

タイトルとURLをコピーしました