はじめに
VBAのユーザーフォームでは、通常、開発時に手動でコントロールを配置します。しかし、「シートのデータ数に応じて、必要な数だけボタンを自動生成したい」といったように、プログラムの実行時にコントロールの数や種類を決めたい場合があります。
VBAの Controls.Add
メソッドを使えば、フォームが既に表示されている状態でも、新しいコントロールをプログラムコードで追加することが可能です。さらに、クラスモジュールを組み合わせることで、動的に追加したコントロールのクリックイベントなどを処理することもできます。
この記事では、少し高度なテクニックになりますが、ユーザーフォームにコマンドボタンを動的に追加し、それぞれのボタンにイベントを割り当てる、パワフルな方法を解説します。
実装手順の全体像
実装は、主に3つのステップで行います。
- コントロールの追加:
Controls.Add
メソッドで、フォーム上に新しいコントロールを生成します。 - クラスモジュールの作成: 動的に追加したボタンのイベントを「監視」するための設計図(クラス)を作成します。
- イベントの紐付け: 追加した各ボタンに対して、クラスの「実体(インスタンス)」を作成し、イベントを処理できるようにします。
動的にボタンを追加し、イベントを処理するVBAサンプルコード
この例では、フォームが表示された後、フォーム自身がクリックされると、A, B, C, D, E という5つのボタンを動的に生成し、各ボタンがクリックされたときに、そのボタンのキャプションをメッセージボックスに表示する処理を実装します。
ステップ1: クラスモジュールを作成する
まず、ボタンのクリックイベントを検知するためのクラスを作成します。
- VBEで「挿入」→「クラスモジュール」を選択します。
- プロパティウィンドウで、
(Name)
をDynamicButtonHandler
に変更します。 - 以下のコードを貼り付けます。
【クラスモジュール: DynamicButtonHandler
】
' --- このクラスが監視するボタンを宣言 ---
Public WithEvents CreatedButton As MSForms.CommandButton
' --- 監視対象のボタンでクリックイベントが発生したときに実行される ---
Private Sub CreatedButton_Click()
' ボタンのキャプションをメッセージボックスに表示
MsgBox "クリックされたボタン: " & CreatedButton.Caption, vbInformation
End Sub
ステップ2: ユーザーフォームのコードを記述する
次に、ユーザーフォーム側で、コントロールの追加とクラスの利用を行うコードを記述します。
【ユーザーフォームのコード】
' --- 作成したクラスのインスタンスを保持するためのコレクション ---
Private ButtonHandlers As Collection
' フォームがクリックされたときにボタンを生成するイベント
Private Sub UserForm_Click()
Dim i As Long
Dim buttonCaptions As Variant
Dim newButton As MSForms.CommandButton
Dim handler As DynamicButtonHandler
' 既にボタンが作成されていたら何もしない
If Me.Controls.Count > 0 Then Exit Sub
' ボタンのキャプションを配列で準備
buttonCaptions = Array("A", "B", "C", "D", "E")
' ハンドラを保持するコレクションを初期化
Set ButtonHandlers = New Collection
' --- ループでボタンを動的に追加 ---
For i = LBound(buttonCaptions) To UBound(buttonCaptions)
' 1. Controls.Addで新しいコマンドボタンを生成
Set newButton = Me.Controls.Add("Forms.CommandButton.1", "DynamicBtn" & i)
' 2. ボタンのプロパティを設定
With newButton
.Caption = buttonCaptions(i)
.Width = 40
.Height = 30
.Top = 20
.Left = 15 + (i * (.Width + 5))
End With
' 3. ボタンに対応するイベントハンドラ(クラスのインスタンス)を作成
Set handler = New DynamicButtonHandler
' 4. ハンドラに、作成したボタンをセットして紐付ける
Set handler.CreatedButton = newButton
' 5. ハンドラをコレクションに追加して、インスタンスが消えないように保持
ButtonHandlers.Add handler
Next i
End Sub
コードの解説
Me.Controls.Add("Forms.CommandButton.1", ...)
これが、コントロールを動的に追加する核心部分です。
Controls.Add
: コントロールを追加するメソッドです。- 第1引数: 追加するコントロールのクラス名を文字列で指定します。
"Forms.CommandButton.1"
はコマンドボタン、"Forms.TextBox.1"
はテキストボックスを指します。 - 第2引数: 追加するコントロールの名前を文字列で指定します。後から操作できるよう、ユニークな名前を付けます。
クラスモジュールと WithEvents
手動で配置したボタンは Private Sub Button_Click()
のように簡単にイベントを記述できますが、動的に追加したコントロールではそれができません。そこで、クラスモジュールと WithEvents
キーワードを使って、特定のコントロールのイベントを監視する仕組みを作ります。詳しい解説はこちらの記事(複数チェックボックスのイベント処理)もご参照ください。
インスタンスの保持
UserForm_Click()
の中で作成したクラスのインスタンス(handler
)は、プロシージャが終了すると消滅してしまいます。それを防ぐため、モジュールレベルで宣言したコレクション変数 ButtonHandlers
にインスタンスを格納し、フォームが閉じられるまでメモリ上に保持し続ける必要があります。
まとめ
今回は、VBAでユーザーフォームにコントロールを動的に追加し、そのイベントをクラスモジュールで処理するという、高度で非常に柔軟なテクニックをご紹介しました。
Controls.Add("クラス名", "名前")
で、実行時にコントロールを生成できる。- 動的に生成したコントロールのイベントは、クラスモジュールと
WithEvents
を使って処理する。 - クラスのインスタンスは、コレクション変数などで保持し続ける必要がある。
この手法をマスターすれば、データの件数に応じて入力欄の数を変えたり、ユーザーの選択に応じて表示するボタンを切り替えたりと、アプリケーションの可能性を大きく広げることができます。