ねらい:ユーザーフォームを「テンプレ化」して、どのプロジェクトでも即使える状態へ
ユーザーフォームは「入力」「確認」「進捗」「設定」などのUIを提供します。毎回ゼロから作ると時間も品質もバラつきます。フォームをテンプレート化し、「初期化」「イベント」「検証」「完了通知」までの枠組みを用意しておくと、初心者でも安全に速く組み込めます。ここでは、使い回しやすいフォームの基本部品とコードを、重要ポイントを深掘りしながら紹介します。
基本設計:フォームは「見せる」、ロジックは「呼ばれる」だけ
- 責務分離:
- フォーム(UI): 入力コントロール配置、値の受け渡し、表示/非表示。
- コントローラ(標準モジュール): 初期化、検証呼び出し、処理本体、完了/エラー表示。
- バリデータ(別モジュール): 値検証(空・型・範囲)。
- 初期化ルール:
- Show前に「デフォルト値」「表示文言」「色/状態」を設定する。
- 閉じ方は統一: OKで「結果返す」、キャンセルで「Nothing/False」を返す。
重要ポイント(深掘り)
- ロジックはフォームに書かない: フォームは「入力UI」。本処理は標準モジュールで行うと再利用性が上がる。
- 値受渡しはプロパティ/メソッドで行う: TextBoxへ直接触らず、フォームの「Get/Set」インターフェース経由にすると壊れにくい。
入力フォームテンプレ(モーダル)— 安全な入力と検証
1) UserForm(名前: FrmInput)構成
- TextBox: txtName, txtPhone, txtScore
- Label: lblTitle
- CommandButton: btnOK, btnCancel
- (任意)Labelでエラーメッセージ: lblError
2) フォーム側コード(最小責務)
' FrmInput コードビハインド
Option Explicit
Private pCanceled As Boolean
Public Property Get Canceled() As Boolean
Canceled = pCanceled
End Property
Public Property Get NameValue() As String
NameValue = Trim$(Me.txtName.Text)
End Property
Public Property Get PhoneValue() As String
PhoneValue = Trim$(Me.txtPhone.Text)
End Property
Public Property Get ScoreValue() As Double
ScoreValue = Val(Me.txtScore.Text)
End Property
Public Sub InitializeUI(ByVal title As String, ByVal defaultName As String)
Me.lblTitle.Caption = title
Me.txtName.Text = defaultName
Me.txtPhone.Text = ""
Me.txtScore.Text = ""
Me.lblError.Caption = ""
End Sub
Private Sub btnOK_Click()
pCanceled = False
Me.Hide ' まず隠す(検証は呼び出し側で実施)
End Sub
Private Sub btnCancel_Click()
pCanceled = True
Me.Hide
End Sub
VB3) コントローラ(標準モジュール)
Option Explicit
' 例:入力→検証→処理→完了メッセージ
Public Sub RunInputProcess()
With FrmInput
.InitializeUI "顧客情報の入力", ""
.Show vbModal
If .Canceled Then
MsgBox "入力をキャンセルしました。": Exit Sub
End If
' --- 検証(部品に分離推奨) ---
Dim errMsg As String
errMsg = ValidateNamePhoneScore(.NameValue, .PhoneValue, .ScoreValue)
If Len(errMsg) > 0 Then
MsgBox "入力エラー: " & errMsg, vbExclamation
Exit Sub
End If
' --- 本処理(例) ---
Worksheets("Input").Cells(2, "A").Value = .NameValue
Worksheets("Input").Cells(2, "B").Value = .PhoneValue
Worksheets("Input").Cells(2, "C").Value = .ScoreValue
End With
Unload FrmInput
MsgBox "登録が完了しました。"
End Sub
Private Function ValidateNamePhoneScore(ByVal nm As String, ByVal ph As String, ByVal sc As Double) As String
' 名前(必須)
If Len(nm) = 0 Then ValidateNamePhoneScore = "氏名は必須": Exit Function
' 電話(数字10~11桁)
Dim digits As String: digits = ExtractDigits(ph)
If Len(digits) < 10 Or Len(digits) > 11 Then ValidateNamePhoneScore = "電話は10~11桁": Exit Function
' スコア(0~100)
If sc < 0 Or sc > 100 Then ValidateNamePhoneScore = "スコアは0~100": Exit Function
End Function
Private Function ExtractDigits(ByVal s As String) As String
Dim i As Long, r As String
For i = 1 To Len(s)
Dim ch As String: ch = Mid$(s, i, 1)
If ch Like "[0-9]" Then r = r & ch
Next
ExtractDigits = r
End Function
VB重要ポイント(深掘り)
- フォームは検証せず「Hide」だけ: 検証はコントローラ。フォームにロジックを入れない。
- プロパティ経由で値取得: TextBoxを外部から直接触らない。変更に強くなる。
進捗フォームテンプレ(モデルレス)— 長処理の安心表示
1) UserForm(名前: FrmProgress)構成
- Label: lblText(進捗テキスト)
- Frame: fraBar(バー枠)
- Label: lblBar(バー本体)
- CommandButton: btnCancel(キャンセル)
2) フォーム側コード
' FrmProgress
Option Explicit
Private pCanceled As Boolean
Public Sub Init(ByVal title As String)
Me.Caption = title
Me.lblText.Caption = "0%"
Me.lblBar.Width = 0
pCanceled = False
End Sub
Public Sub UpdateProgress(ByVal cur As Long, ByVal total As Long)
If total <= 0 Then Exit Sub
Dim pct As Double: pct = cur / total
Me.lblText.Caption = Format(pct, "0%") & " (" & cur & "/" & total & ")"
Me.lblBar.Width = Me.fraBar.Width * pct
DoEvents ' UI応答
End Sub
Public Property Get Canceled() As Boolean
Canceled = pCanceled
End Property
Private Sub btnCancel_Click()
pCanceled = True
End Sub
VB3) コントローラ(標準モジュール)
Option Explicit
Public Sub RunLongProcessWithProgress()
Dim total As Long: total = 30000
FrmProgress.Init "長処理(進捗表示)"
FrmProgress.Show vbModeless
Dim i As Long
Application.ScreenUpdating = False
For i = 1 To total
' --- 本処理例 ---
Dim s As String: s = "テスト" & i
' -----------------
If i Mod 200 = 0 Then
FrmProgress.UpdateProgress i, total
If FrmProgress.Canceled Then
Application.ScreenUpdating = True
Unload FrmProgress
MsgBox "処理を中断しました。": Exit Sub
End If
End If
Next
Application.ScreenUpdating = True
FrmProgress.UpdateProgress total, total
Unload FrmProgress
MsgBox "処理完了!"
End Sub
VB重要ポイント(深掘り)
- vbModelessで操作継続: 進捗を出しながらキャンセルも受け付ける。
- 更新間隔を調整: 毎回更新は遅い。200件ごとなどで間引く。
- 後片付け徹底: Unload+ScreenUpdating ON。
確認ダイアログテンプレ(Yes/No/Cancel)— 一貫したUX
1) UserForm(名前: FrmConfirm)構成
- Label: lblMessage
- CommandButton: btnYes, btnNo, btnCancel
2) フォーム側
' FrmConfirm
Option Explicit
Private pResult As VbMsgBoxResult
Public Sub Ask(ByVal msg As String)
Me.lblMessage.Caption = msg
pResult = vbCancel
End Sub
Public Property Get Result() As VbMsgBoxResult
Result = pResult
End Property
Private Sub btnYes_Click(): pResult = vbYes: Me.Hide: End Sub
Private Sub btnNo_Click(): pResult = vbNo: Me.Hide: End Sub
Private Sub btnCancel_Click(): pResult = vbCancel: Me.Hide: End Sub
VB3) 使い方
Sub Example_Confirm()
With FrmConfirm
.Ask "上書きしてもよいですか?"
.Show vbModal
Select Case .Result
Case vbYes: MsgBox "上書きします。"
Case vbNo: MsgBox "スキップします。"
Case vbCancel: MsgBox "中止しました。"
End Select
End With
Unload FrmConfirm
End Sub
VB重要ポイント(深掘り)
- MsgBox互換の戻り値: 呼び出し側が分岐しやすい。
- 表示専用: 本処理は一切入れない。UIの再利用性が高い。
設定フォームテンプレ(Config編集)— フォームで安全に値変更
1) UserForm(名前: FrmConfig)構成
- TextBox: txtOutputFolder, txtThreshold
- CheckBox: chkEnableLog
- CommandButton: btnSave, btnCancel
2) フォーム側
' FrmConfig
Option Explicit
Public Sub LoadConfig()
Me.txtOutputFolder.Text = ThisWorkbook.Worksheets("Config").Range("B2").Value ' OUTPUT_FOLDER
Me.txtThreshold.Text = ThisWorkbook.Worksheets("Config").Range("B3").Value ' THRESHOLD
Me.chkEnableLog.Value = (UCase$(ThisWorkbook.Worksheets("Config").Range("B4").Value) = "TRUE")
End Sub
Public Function ValidateAndSave() As Boolean
Dim folder As String: folder = Trim$(Me.txtOutputFolder.Text)
If Len(folder) = 0 Then MsgBox "フォルダは必須": Exit Function
If Not IsNumeric(Me.txtThreshold.Text) Then MsgBox "閾値は数値": Exit Function
ThisWorkbook.Worksheets("Config").Range("B2").Value = folder
ThisWorkbook.Worksheets("Config").Range("B3").Value = CDbl(Me.txtThreshold.Text)
ThisWorkbook.Worksheets("Config").Range("B4").Value = IIf(Me.chkEnableLog.Value, "TRUE", "FALSE")
ValidateAndSave = True
End Function
Private Sub btnSave_Click()
If ValidateAndSave() Then Me.Hide
End Sub
Private Sub btnCancel_Click()
Me.Hide
End Sub
VB3) 使い方
Sub ShowConfigEditor()
FrmConfig.LoadConfig
FrmConfig.Show vbModal
Unload FrmConfig
MsgBox "設定の読み直しが必要な処理は、次回実行時に反映されます。"
End Sub
VB重要ポイント(深掘り)
- ロード/セーブを分離: 読み込み→編集→妥当性検証→保存の流れを固定。
- Configの「人向け編集」を安全化: フォーム側で型チェック。
テンプレの使い回しコツと落とし穴
- 使い回しのコツ:
- フォームの公開APIを固定: InitializeUI/UpdateProgress/Askなど、名前と引数を揃える。
- スタイル統一: フォント・色・余白・ボタン配列を「共通ルール」に。
- イベントの最小化: フォームはクリック/変更イベントのみ。計算ロジックは呼び出し側。
- 落とし穴と対策:
- フォームに処理を詰め込みすぎる:
- 対策: すべてコントローラへ。フォームは状態と表示だけ。
- モデルレスでHide忘れ/Unload忘れ:
- 対策: 終了時は必ず Unload。必要なら Finally的後片付け関数を共通化。
- TextBoxの値型ズレ:
- 対策: 取得はプロパティで Trim/Val/IsNumeric 検証を組み込む。
- 検証エラーメッセージが分かりにくい:
- 対策: フォーム内lblErrorやMsgBoxで「何がダメか」を具体的に。
- フォームに処理を詰め込みすぎる:
例題で練習(貼って試せる)
- 例1: FrmInput+RunInputProcess → 入力→検証→シート書き込みの流れ。
- 例2: FrmProgress+RunLongProcessWithProgress → 進捗バー表示とキャンセル体験。
- 例3: FrmConfirm → Yes/No/Cancelの分岐を確認。
- 例4: FrmConfig → Configをフォームで編集し、安全に保存。
スターター手順(最短導入)
- 目的別フォームを4種(入力/進捗/確認/設定)作り、公開API(InitializeUI/UpdateProgress/Ask/LoadConfig)を揃える。
- コントローラ側に共通枠(初期化・検証・本処理・後片付け)を用意して、フォームは呼ぶだけにする。
- 検証関数を標準モジュールへ分離し、同じロジックを全フォームで使い回す。
- Unload/ScreenUpdating/StatusBarなどの後片付けを必ず入れる。
- スタイルと文言の共通化で、ユーザー体験を「いつも同じ」に。
