ユーザーフォーム入門
第22回.コントロールの動的作成

Excelマクロのユーザーフォームの基礎、エクセルVBAの入門解説
最終更新日:2020-01-06

第22回.コントロールの動的作成


ユーザーフォームは、事前に画面デザインを考えて、それにそって部品コントロールを配置するものです。
しかし、ユーザーの操作に応じて、動的に部品コントロールを変更したい場合があります。


このような場合の対処方法としては、大きく以下の2通りの方法があります。
・部品コントロールの表示/非表示を切り替える
・動的に部品コントロールを追加/削除する

以下それぞれの方法について、具体的なVBAコードサンプルとともに解説していきます。

コントロールの動的作成の動作説明

初期

VBA マクロ ユーザーフォーム 動的コントロール

初期はコンボボックスだけの表示にします。
コンボボックスは1~5が選択できるようにします。

コンボボックスの選択

VBA マクロ ユーザーフォーム 動的コントロール

コンボボックスで選択した数値に応じてチェックボックスを表示します。

チェックボックスのチェック

VBA マクロ ユーザーフォーム 動的コントロール

チェックボックスをチェックするとテキストボックスを表示します。
チェックを外すとテキストボックスを消します。

コントロールの表示/非表示を切り替える

コントロールの表示/非表示を切り替えるには、以下の2通りの方法があります。
・Visibleプロパティの値をTrueで表示/Falseで非表示
・コントロールをフォーム外へ移動することで非表示/所定位置に移動することで表示

コントロールをフォーム外に移動して非表示にする方法は、少々裏技的な感じもしますが時々使われる手段なので覚えておくと良いでしょう。
以下では、Visibleプロパティの値を変更する方法でのVBAサンプルになります。

VBA マクロ ユーザーフォーム 動的コントロール

コントロールの名称

コンボボックス:cmb件数
チェックボックス:chk01~chk05
テキストボックス:txt01~txt05

フォームモジュール

モジュール名:何でも構いません。標準モジュールでの.Showに合わせてください。



Option Explicit

'初期処理
Private Sub UserForm_Initialize()
  With Me.cmb件数
    .Style = fmStyleDropDownList
    .List = Array(0, 1, 2, 3, 4, 5)
  End With
  Call ControlsUnVisibleAll
End Sub

'閉じるボタン
Private Sub btClose_Click()
  Unload Me
End Sub

'コンボボックスで数値を選択
Private Sub cmb件数_Change()
  Dim i As Long
  For i = 1 To Me.cmb件数.Value
    Me.Controls("chk" & Format(i, "00")).Visible = True
  Next
  Call ControlsUnVisible(Me.cmb件数.Value)
End Sub

'チェックボックスのチェックを変更
Private Sub chk01_Change()
  Me.txt01.Visible = Me.chk01.Value
End Sub
Private Sub chk02_Change()
  Me.txt02.Visible = Me.chk02.Value
End Sub
Private Sub chk03_Change()
  Me.txt03.Visible = Me.chk03.Value
End Sub
Private Sub chk04_Change()
  Me.txt04.Visible = Me.chk04.Value
End Sub
Private Sub chk05_Change()
  Me.txt05.Visible = Me.chk05.Value
End Sub

'指定数値以降のチェックボックスとテキストボックスを非表示
Private Sub ControlsUnVisible(Optional ByVal cnt As Long = 0)
  Dim i As Long
  For i = cnt + 1 To 5
    Me.Controls("chk" & Format(i, "00")).Visible = False
    Me.Controls("chk" & Format(i, "00")).Value = False
    Me.Controls("txt" & Format(i, "00")).Visible = False
  Next
End Sub

'全てのチェックボックスとテキストボックスを非表示
Private Sub ControlsUnVisibleAll()
  Dim ctl As Control
  For Each ctl In Me.Controls
    Select Case TypeName(ctl)
      Case "CheckBox", "TextBox"
        ctl.Visible = False
    End Select
  Next
End Sub

標準モジュールは、
フォームモジュール名.Show
これだけで良いです。
テストするだけならフォームモジュールでF5でも構いません。
実際に動かしながら、動作と対応するVBAコードを確認してみてください。

動的に部品コントロールを追加/削除する

動的に表示を切り替えるコントロールをVBAで追加/削除します。
VBAクラスでWithEventsを使います。
VBAクラスを使う事で、ユーザー定義イベントを作成したり、動的にイベントを割り当てる事が出来ます。ユーザー独自のイベントを作成したり、フォームに動的に追加したコントロールにイベントを設定することができます。EventステートメントとRaiseEventステートメントを使う事で、ユーザー定義イベントつまりユーザー独自のイベントを作成できます。

VBA マクロ ユーザーフォーム 動的コントロール

コントロールの名称

コンボボックス:cmb件数

クラスモジュール

モジュール名:clsEvant

Option Explicit

'イベントを発生させるクラス(オブジェクト)
Private WithEvents pCheckBox As MSForms.CheckBox

'コントロールの親であるフォーム
Private pForm As MSForms.UserForm

'対応するコントロールを設定するプロパティ
Public Property Set CheckBox(ByVal aCheckBox As MSForms.CheckBox)
  Set pCheckBox = aCheckBox
  Set pForm = aCheckBox.Parent
End Property

'WithEventsのイベントプロシージャー
Private Sub pCheckBox_Change()
  Dim sNum As String
  sNum = Right(pCheckBox.Name, 2)
  
  Dim txt As MSForms.TextBox
  Set txt = GetTextBox(sNum)
  If txt Is Nothing Then
    Set txt = pForm.Add("Forms.TextBox.1", "txt" & sNum)
    With txt
      '以下の数値はサンプル画像で使った数値です
      .Top = sNum * 21 + 13
      .Left = 30
      .Height = 16.8
      .Width = 168
    End With
  End If
  
  txt.Visible = pCheckBox.Value
End Sub

'テキストボックスの存在確認:エラーを使った簡易コードにしています
Private Function GetTextBox(ByVal sNum As String) As MSForms.CheckBox
  On Error Resume Next
  Set GetTextBox = pForm.Controls("txt" & sNum)
End Function

WithEventsのイベントプロシージャーは、
WithEvents変数名_イベント名
これはVBAの規則となっています。

また、WithEventsに指定するクラス(オブジェクト)の型に注意してください。
MSForms.CheckBox
このように、MSFormsのメンバを使わなければなりません。
単にCheckBoxではエラーとなります。

VBA マクロ コントロールの動的作成

フォームモジュール

モジュール名:何でも構いません。標準モジュールでの.Showに合わせてください。



Option Explicit

Private clsAry() As clsEvent

'初期処理
Private Sub UserForm_Initialize()
  With Me.cmb件数
    .List = Array(0, 1, 2, 3, 4, 5)
  End With
  ReDim clsAry(1 To 5)
End Sub

'閉じるボタン
Private Sub btClose_Click()
  Unload Me
End Sub

'コンボボックスで数値を選択
Private Sub cmb件数_Change()
  Dim ctl As Control
  Dim max As Long
  Dim num As Long
  max = Me.cmb件数.Value
  For Each ctl In Me.Controls
    If IsNumeric(Right(ctl.Name, 2)) Then
      num = Right(ctl.Name, 2)
      If num > max Then
        'コントロールの削除
        Me.Controls.Remove ctl.Name
      End If
    End If
  Next
  
  'チェックボックスの動的追加
  For num = 1 To max
    Call AddCheckBox(num)
  Next
End Sub

'チェックボックスの動的追加
Private Function AddCheckBox(ByVal num As Long) As MSForms.CheckBox
  'チェックボックスの存在確認
  Set AddCheckBox = GetCheckBox(num)
  If Not AddCheckBox Is Nothing Then Exit Function
  
  'チェックボックスをフォームに追加
  Dim sName As String
  sName = "chk" & Format(num, "00")
  Set AddCheckBox = Me.Controls.Add("Forms.CheckBox.1", sName)
  With AddCheckBox
    .Top = num * 21 + 13
    .Left = 10
    .Height = 16.8
    .Width = 18
    .Caption = ""
  End With
  
  '追加したチェックボックスをクラスに設定
  Set clsAry(num) = New clsEvent
  Set clsAry(num).CheckBox = AddCheckBox
End Function

'チェックボックスの存在確認
Private Function GetCheckBox(ByVal num As Long) As MSForms.CheckBox
  On Error Resume Next
  Set GetCheckBox = Me.Controls("chk" & Format(num, "00"))
End Function

標準モジュールは、
フォームモジュール名.Show
これだけで良いです。
テストするだけならフォームモジュールでF5でも構いません。
実際に動かしながら、動作と対応するVBAコードを確認してみてください。

コントロールの動的作成の最後に

上記で2通りの方法を紹介しましたが、
基本的には、「コントロールの表示/非表示を切り替える」こちらの方法が良いでしょう。

チェックボックスの数を増減させたい場合でも、
同時に表示できる数には限度がありますし、最大数は決まっているはずです。
最大数をあらかじめ配置しておき、表示/非表示での切替と位置移動だけで全て対応できます。

何より、よりきめ細かい動作をさせようとすると、動的な追加ではVBAが複雑になりデバッグが大変になってしまいます。
とはいえ、コントロールの動的追加はユーザーフォームを扱う上では必要な技術ですので、いざと言う時に使えるように習得しておくと良いでしょう。



同じテーマ「ユーザーフォーム入門」の記事

第14回.オプションボタン(OptionButton)の追加
第15回.ここまでの整理と全VBA
第16回.アクティブコントロールに色を付ける
第17回.Enterキーで次のコントロールに移動する
第18回.2段階のコンボボックス
第19回.数値専用のテキストボックス
第20回.テキストボックスの各種イベント
第21回.ユーザーフォームの各種イベント
第22回.コントロールの動的作成
第23回.イベントプロシージャーの共通化
第24回.イベントプロシージャーの共通化(Enter,Exit)


新着記事NEW ・・・新着記事一覧を見る

ツイッターで出されたVBAのお題をやってみた|エクセル雑感(1月13日)
イベントプロシージャーの共通化(Enter,Exit)|ユーザーフォーム入門(1月13日)
Rangeオブジェクトの論理演算(差集合と排他的論理和)|VBA技術解説(1月10日)
イベントプロシージャーの共通化|ユーザーフォーム入門(1月7日)
コントロールの動的作成|ユーザーフォーム入門(1月6日)
Evaluateメソッド(文字列の数式を実行します)|VBA技術解説(1月5日)
エクスポート(PDF/XPS)|VBA入門(1月2日)
分析関数(OVER句,WINDOW句)|SQL入門(12月25日)
取得行数を限定するLIMIT句|SQL入門(12月21日)
外部ライブラリ(ActiveXオブジェクト)|VBA入門(12月21日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.RangeとCellsの使い方|VBA入門
3.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
4.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
5.変数宣言のDimとデータ型|VBA入門
6.繰り返し処理(For Next)|VBA入門
7.マクロって何?VBAって何?|VBA入門
8.セルに文字を入れるとは(Range,Value)|VBA入門
9.空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)|VBA技術解説
10.ひらがな⇔カタカナの変換|エクセル基本操作




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。



このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ