Excel VBA 逆引き集 | フォームテンプレート化

Excel VBA
スポンサーリンク

ねらい:ユーザーフォームを「テンプレ化」して、どのプロジェクトでも即使える状態へ

ユーザーフォームは「入力」「確認」「進捗」「設定」などの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
VB

3) コントローラ(標準モジュール)

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
VB

3) コントローラ(標準モジュール)

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
VB

3) 使い方

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
VB

3) 使い方

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をフォームで編集し、安全に保存。

スターター手順(最短導入)

  1. 目的別フォームを4種(入力/進捗/確認/設定)作り、公開API(InitializeUI/UpdateProgress/Ask/LoadConfig)を揃える。
  2. コントローラ側に共通枠(初期化・検証・本処理・後片付け)を用意して、フォームは呼ぶだけにする。
  3. 検証関数を標準モジュールへ分離し、同じロジックを全フォームで使い回す。
  4. Unload/ScreenUpdating/StatusBarなどの後片付けを必ず入れる。
  5. スタイルと文言の共通化で、ユーザー体験を「いつも同じ」に。

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