TL;DR — UserForm 上のすべてのコントロールは一箇所に住んでいます。フォームの
Controlsコレクションです。Me.Controlsとして到達し、For Eachでループし、どのコントロールにも 名前を文字列として 指定してアクセス できます —Me.Controls("txtName")。このコレクションのおかげで、二十項目のフォームも短く書けます。二十行の名 前付き参照の代わりに一つのループで済むのです。コレクションにはラベルやFrame、ボタンも入っているので、目当て のコントロールだけにTypeNameやTypeOfで絞り込みましょう。
Private Sub cmdClear_Click()
Dim ctl As MSForms.Control
' フォーム上のすべてのテキストボックスを一つのループで空にする:
For Each ctl In Me.Controls
If TypeOf ctl Is MSForms.TextBox Then ctl.Value = ""
Next ctl
End Sub
一つのコントロールには、名前をそのまま書いて到達します — txtName.Value、lstItems.Clear。これはコードを書く
時点でそのコントロールがわかっている場合の話です。Controls コレクションが要るのは、わからない場合 — すべて
の テキストボックスが欲しい、あるいは実行時に名前を組み立てるコントロールが欲しい、というときです。ここは
UserForm コントロール一家のハブであり、ここにある三つの記事でその一家が完成します。Controls コレクションはコ
ントロールをまとめて扱う方法、TextBox は常に String を返すので入力された数値も変換する
まで文字列のまま、そして Frame はオプションボタンの一組にそれぞれのグループを与えるコン
テナーです。コレクション、文字列、コンテナーを押さえれば、個々のコントロール —
ListBox、ComboBox、CheckBox — もす
べて同じ読み方をします。コントロールとは、フォームを通して到達し、プロパティを通して話しかけるオブジェクトなの
です。
この記事で学べること
Me.Controlsとは何か、そしてフォームが画面ではなくコレクションである理由For Eachですべてのコントロールをループし、目当てのものだけに作用させる方法TypeNameとTypeOfで型を絞り込み、ラベルやFrameを読み飛ばす方法- データから組み立てたフォームのために、名前を文字列としてコントロールに到達する方法
- フォームの実行中にコントロールを追加する方法
- 判断のしどころ:ループが二十個の名前付き参照に勝るのはいつか
考え方の軸:フォームはコントロールオブジェクトのコレクション
UserForm は画面のように見えますが、コードから見れば コレクション です。Me.Controls がそのコレクションで
す — フォームに置いたすべてのテキストボックス、ラベル、ボタン、Frame、リストボックスが、数えたりループしたり
できるオブジェクトとして入っています。Me.Controls.Count でいくつあるかがわかり、Me.Controls(0) が最初の一
つです(コレクションは 0 始まりです)。Me はコードが走っているフォーム自身を指すので、UserForm 自身のモジュー
ルの中では Me.Controls と Controls は同じ意味になります。
MsgBox "This form has " & Me.Controls.Count & " controls"
フォームをコレクションとして見えるようになると、フォームコードの繰り返し部分は消えていきます。txt1、txt2、
txt3 と書く代わりに、ループを書くようになるのです。
For Each ですべてのコントロールをループする
For Each はコレクションを一つずつ歩きます。コントロール変数の型は MSForms.Control — すべてのコントロールが
共有する汎用の型です。
Dim ctl As MSForms.Control
For Each ctl In Me.Controls
Debug.Print ctl.Name
Next ctl
これが「フォームをクリアする」「すべての値を集める」「作業中は全部を無効にする」の裏にあるパターンです。しかし
コレクションには すべての コントロールが入っています — 意図していないものも含めて — ですから ctl.Value を書
くだけの素朴なループは、最初のラベルでクラッシュします。ラベルには Value がないからです。それが次の問題です。
TypeName と TypeOf で型を絞り込む
すべてのコントロールに欲しいプロパティがあるとは限りません。Label に .Value はなく、CommandButton に
.Text はありません。間違ったものに触れると実行時エラー 438、Object doesn't support this property or
method になります。ですから先に絞り込みましょう。TypeName(ctl) は型を文字列として返し、TypeOf ctl Is は直
接それを調べます。
For Each ctl In Me.Controls
If TypeOf ctl Is MSForms.TextBox Then
ctl.Value = "" ' 安全:この行に到達するのはテキストボックスだけ
End If
Next ctl
If の中では TypeOf ... Is を使いましょう — それが定石の調べ方です。型名をメッセージに出したい、あるいは複数
の型にわたる Select Case を書きたいときは TypeName(ctl) に手を伸ばします。ルールはこうです:Controls に対
する For Each の中でプロパティを呼ぶ前に、そのコントロールがそれを持つ型だと先に証明すること。
名前でコントロールに到達する
ここでコレクションが報われます。名前の文字列 でインデックスでき、しかもその文字列は実行時に組み立てられま す。
Dim i As Long
For i = 1 To 5
Me.Controls("txtDay" & i).Value = "" ' txtDay1 ... txtDay5
Next i
txtDay1 から txtDay5 まで名前を付けたフィールドを持つフォームには、五行ではなく一つのループで済みます。これ
がデータ駆動のフォームの仕組みです — 名前は規則に従い、コードがそれを組み立てます。落とし穴は、リフレクション一
家にある Application.Run や CallByName の罠と同じです — 名前は 文字列 なので、コンパイラはチェックできま
せん。"txtDya1" のような打ち間違いは赤い波線にはならず、その行が実行されたときの実行時エラーになります。名前
はわかっているパターンから組み立て、どこかがデータ由来なら On Error でガードしまし
ょう。
フォームの実行中にコントロールを追加する
コレクションは固定されていません。Controls.Add はフォームが開いている間にコントロールを作成します。これが、
データに応じてサイズが決まるフォーム — レコードごとに一行のフィールド — を組み立てる方法です。
Dim box As MSForms.TextBox
Set box = Me.Controls.Add("Forms.TextBox.1", "txtDynamic", True)
box.Top = 10: box.Left = 10: box.Width = 120
最初の引数はコントロールのクラス、二番目はその名前、三番目は表示するかどうかです。動的に追加したコントロール
には、WithEvents クラスで配線しない限りコードビハインドのイベントはありません — 実在するテクニックですが、固
定レイアウトでは本当にどうにもならないときだけ手を伸ばすべきものです。
判断のしどころ:ループが二十個の名前付き参照に勝るのはいつか
Controls コレクションは強力で、だからこそ使いすぎやすいのです。正直なルールはこうです。名前がわかっているコ
ントロールが数個なら、txtName.Value のほうが Me.Controls("txtName").Value より明快で、コンパイラもチェッ
クしてくれます。ループが場を得るのは、コントロールが 数多く、しかも均一 なとき — 二十個の日付フィールド、グ
リッド状の入力欄、フォーム上のすべてのテキストボックス — か、名前が本当にデータで決まるときです。三個のコント
ロールをループするのは見せびらかしですが、二十個をループするのはそれが唯一まともな書き方です。実際に繰り返し
を取り除くときだけコレクションを選び、それができると証明するためだけには選ばないでください。
ExcelMaster の活用
ここでの失敗は静かで、遅れてやってきます。忘れていた一つのラベルでクラッシュする For Each、もう一致しなくな
ったデータから組み立てたコントロール名、間違った型に対して呼ばれたプロパティ。そのどれもが、完成したように見え
たフォームの実行時に、同じエラー 438 や「見つかりません」として表面化します。
ExcelMaster には、フォームの振る舞いを
説明するだけで構いません — 「すべてのテキストボックスをクリアする」「すべての入力を一行に読み込む」「一覧から
五つの日付フィールドを組み立てる」 — すると型フィルターと名前ガードをすでに組み込んだ Controls ループを書い
てくれます。フォームとコードはあなたの手元に残り、実行時のバグ探しをせずに済みます。
よくある質問
VBA の Controls コレクションとは何ですか?
UserForm 上のすべてのコントロールのコレクションで、Me.Controls として到達します。各項目はコントロールオブジ
ェクトです — テキストボックス、ラベル、ボタン、Frame、リストボックスなど。.Count で数え、For Each でルー
プし、インデックスまたは名前の文字列で一つに到達できます。一つずつ名前を挙げる代わりに、多くのコントロールへ一
度に作用させる標準的な方法です。
UserForm 上のすべてのコントロールをループするには?
変数を As MSForms.Control で宣言し、For Each ctl In Me.Controls を使います。ループの中では、プロパティに
触れる前に TypeOf ctl Is MSForms.TextBox で型を調べましょう。コレクションにはそのプロパティを持たず、エラー
438 を発生させるラベルやボタンも入っているからです。
文字列に入っている名前でコントロールを参照するには?
その文字列でコレクションをインデックスします:Me.Controls("txtName")。名前は Me.Controls("txtDay" & i) の
ように実行時に組み立てることもできます。名前は文字列なので、打ち間違いはその行が実行されるまで捕まりません —
「見つかりません」というエラーになります — ですから、わかっているパターンから名前を組み立て、ガードしましょう。
Controls に対する For Each ループがエラー 438 を発生させるのはなぜですか?
今のコントロールが持っていないプロパティを呼んだからです — もっとも多いのは Label、Frame、CommandButton
に対する .Value や .Text です。Controls コレクションにはすべての型が入っているので、そのプロパティを読み
書きする前に TypeOf ctl Is MSForms.TextBox(または目当ての型)で絞り込みましょう。
フォームの実行中にコントロールを追加できますか?
できます。Me.Controls.Add("Forms.TextBox.1", "txtNew", True) が実行時に一つ作成します。これが、データに合わ
せたサイズのフォームを組み立てる方法です。そのあとで Top、Left、Width を設定します。動的に追加したコント
ロールには、WithEvents クラスでつながない限りイベントハンドラーがありません。
検証環境
検証環境: Excel 365 (Windows 11)、VBA 7.1 — 最終確認 2026-09-28。
関連ガイド: VBA TextBox · VBA Frame · VBA UserForm · VBA ListBox · VBA ComboBox · VBA CheckBox · VBA On Error
