Excel VBA 逆引き集 | 実務テンプレ完全版(超再利用部品) – バリデーション総合テンプレ

Excel VBA Excel VBA
スポンサーリンク

ねらい:あらゆる「入力ミス」「設定ミス」を一箇所で検査できる型を持つ

バリデーション総合テンプレのゴールは、こうです。
「動かしてみたら途中で落ちた」「設定ミスに気づくのが遅い」をやめて、
マクロを本処理に入る前に「おかしなところがないか」を一気にチェックできる“関所”を作ることです。

ここでは、次の3つをまとめて扱えるテンプレを作ります。

シートやセルの「入力値チェック」
設定シート(Config系)の「設定内容チェック」
フォーム入力の「UIバリデーション」

全部をバラバラに書くのではなく、「バリデーション専用の考え方とコードの型」を決めておく——これがポイントです。


全体設計:バリデーションを「専用モジュール+専用関数」に切り出す

なぜ“専用モジュール”にするのか

バリデーションを各所にバラバラに書くと、こうなります。

どこで何をチェックしているか分からない
同じチェックを何度も書く
仕様変更のたびに、あちこち直す羽目になる

これを避けるために、「バリデーションは Validation モジュールに集約する」というルールを決めます。

例として、こんなモジュール構成をイメージしてください。

ModValidation
入力値チェック・設定チェック・共通チェックをまとめるモジュール

そして、各処理の入口で必ずこうします。

本処理の前に「Validate○○」を呼ぶ
False(失敗)なら、その場で中断してメッセージを出す
True(成功)なら、本処理に進む

この「必ず関所を通る」という型を作るのが、総合テンプレの核です。


コア部品1:シート入力バリデーションテンプレ

例題:顧客マスタシートの入力チェック

顧客マスタシート(Customer)に、こんな列があるとします。

A列:顧客コード(必須・重複禁止)
B列:顧客名(必須)
C列:売上(数値・0以上)

このシートを使ってJOINや集計をする前に、「入力としておかしくないか」をチェックしたい、という場面です。

バリデーション関数の型

ModValidation に、次のような関数を用意します。

' ModValidation.bas
Option Explicit

Public Function ValidateCustomerSheet() As Boolean
    Dim ws As Worksheet
    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets("Customer")
    On Error GoTo 0
    
    If ws Is Nothing Then
        MsgBox "Customer シートが存在しません。", vbExclamation
        ValidateCustomerSheet = False
        Exit Function
    End If
    
    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    If lastRow < 2 Then
        MsgBox "Customer シートにデータがありません。", vbExclamation
        ValidateCustomerSheet = False
        Exit Function
    End If
    
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")
    
    Dim i As Long
    For i = 2 To lastRow
        Dim code As String
        code = Trim(CStr(ws.Cells(i, "A").Value))
        
        Dim name As String
        name = Trim(CStr(ws.Cells(i, "B").Value))
        
        Dim sales As Variant
        sales = ws.Cells(i, "C").Value
        
        If code = "" Then
            MsgBox "Customer!A" & i & " 顧客コードが空です。", vbExclamation
            ValidateCustomerSheet = False
            Exit Function
        End If
        
        If dict.Exists(code) Then
            MsgBox "顧客コードが重複しています: " & code & "(行 " & i & ")", vbExclamation
            ValidateCustomerSheet = False
            Exit Function
        Else
            dict.Add code, True
        End If
        
        If name = "" Then
            MsgBox "Customer!B" & i & " 顧客名が空です。", vbExclamation
            ValidateCustomerSheet = False
            Exit Function
        End If
        
        If Not IsNumeric(sales) Or sales < 0 Then
            MsgBox "Customer!C" & i & " 売上が数値でないか、0未満です。", vbExclamation
            ValidateCustomerSheet = False
            Exit Function
        End If
    Next i
    
    ValidateCustomerSheet = True
End Function
VB

ここでの重要ポイントをかみ砕きます。

シートの存在チェック
データ行があるかチェック
行ループで「必須」「重複」「型・範囲」をチェック
エラーを見つけたら、その場でメッセージを出し、False を返して終了

「最初のエラーで止める」か「全部のエラーを集める」かは好みですが、
まずは“最初のエラーで止める”型の方がシンプルで実務向きです。

本処理からの呼び出し方

顧客マスタを使う処理の入口で、こう書きます。

Sub JoinCustomer()
    Const MODULE_NAME As String = "JoinCustomer"
    On Error GoTo ErrHandler
    
    If Not ValidateCustomerSheet() Then
        Exit Sub
    End If
    
    ' ここからJOIN本処理
    
    Exit Sub
ErrHandler:
    LogError MODULE_NAME, "MAIN", Err
End Sub
VB

「Validate○○がTrueなら本処理へ」というパターンを徹底すると、
どの処理も“必ず関所を通る”構造になります。


コア部品2:設定シート(Config系)のバリデーションテンプレ

なぜ設定シートのチェックが重要か

設定シート方式のツール(JOIN・集計・変換・差分など)は、
「設定が間違っていると、コードは正しくても結果が壊れる」という性質があります。

だからこそ、「Config○○を読み込んだ直後に、設定内容を検査する」テンプレを持っておくと強いです。

例題:JOIN設定(ConfigJoin)のバリデーション

ConfigJoin に、こんな列があるとします。

A列:Enabled(Y/N)
B列:LeftSheet
C列:LeftKeyCol
D列:RightSheet
E列:RightKeyCol
F列:JoinType(INNER/LEFT など)

これを読み込んだ後に、「シートが存在するか」「列指定が正しいか」「JoinType が許可された値か」をチェックします。

ルール配列を検査する関数

まず、JOINルールの構造体を想定します。

Private Type JoinRule
    Enabled As Boolean
    LeftSheet As String
    LeftKeyCol As Long
    RightSheet As String
    RightKeyCol As Long
    JoinType As String
End Type
VB

LoadJoinRules で rules As Variant(JoinRule配列)を読み込んだ後、
次のような検証関数を呼びます。

Public Function ValidateJoinRules(ByRef rules As Variant) As Boolean
    Dim i As Long
    For i = LBound(rules) To UBound(rules)
        If rules(i).Enabled Then
            If Not SheetExists(rules(i).LeftSheet) Then
                MsgBox "JOIN設定エラー: LeftSheet [" & rules(i).LeftSheet & "] が存在しません。(ルール " & i & ")", vbExclamation
                ValidateJoinRules = False
                Exit Function
            End If
            
            If Not SheetExists(rules(i).RightSheet) Then
                MsgBox "JOIN設定エラー: RightSheet [" & rules(i).RightSheet & "] が存在しません。(ルール " & i & ")", vbExclamation
                ValidateJoinRules = False
                Exit Function
            End If
            
            If Not ColumnExists(rules(i).LeftSheet, rules(i).LeftKeyCol) Then
                MsgBox "JOIN設定エラー: LeftKeyCol が不正です。(ルール " & i & ")", vbExclamation
                ValidateJoinRules = False
                Exit Function
            End If
            
            If Not ColumnExists(rules(i).RightSheet, rules(i).RightKeyCol) Then
                MsgBox "JOIN設定エラー: RightKeyCol が不正です。(ルール " & i & ")", vbExclamation
                ValidateJoinRules = False
                Exit Function
            End If
            
            Select Case UCase$(rules(i).JoinType)
                Case "INNER", "LEFT"
                    ' OK
                Case Else
                    MsgBox "JOIN設定エラー: JoinType [" & rules(i).JoinType & "] は許可されていません。(ルール " & i & ")", vbExclamation
                    ValidateJoinRules = False
                    Exit Function
            End Select
        End If
    Next i
    
    ValidateJoinRules = True
End Function
VB

ここで使っている補助関数も、ModValidation か ModConfigUtil に置いておきます。

Public Function SheetExists(ByVal sheetName As String) As Boolean
    On Error Resume Next
    SheetExists = Not ThisWorkbook.Worksheets(sheetName) Is Nothing
    On Error GoTo 0
End Function

Public Function ColumnExists(ByVal sheetName As String, ByVal colNum As Long) As Boolean
    Dim ws As Worksheet
    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets(sheetName)
    On Error GoTo 0
    If ws Is Nothing Then
        ColumnExists = False
    Else
        ColumnExists = (colNum >= 1 And colNum <= ws.Columns.Count)
    End If
End Function
VB

JOINエンジン側では、こう使います。

Public Sub RunJoinTool()
    Dim rules As Variant
    rules = LoadJoinRules()
    If IsEmpty(rules) Then Exit Sub
    
    If Not ValidateJoinRules(rules) Then
        Exit Sub
    End If
    
    ' ここからJOIN本処理
End Sub
VB

「設定を読んだら、必ず Validate○○Rules を通す」という型を作るのが、総合テンプレの大事な部分です。


コア部品3:フォーム入力のバリデーションテンプレ

例題:日付+モード選択フォーム

前に作った frmMain(txtDate, cboMode)を思い出してください。
フォーム側の ValidateInput を、バリデーションテンプレとして整理します。

Private Function ValidateInput() As Boolean
    If Trim(Me.txtDate.Value) = "" Then
        MsgBox "対象日を入力してください。", vbExclamation
        Me.txtDate.SetFocus
        ValidateInput = False
        Exit Function
    End If
    
    If IsDate(Me.txtDate.Value) = False Then
        MsgBox "対象日が日付として正しくありません。", vbExclamation
        Me.txtDate.SetFocus
        ValidateInput = False
        Exit Function
    End If
    
    If Me.cboMode.ListIndex < 0 Then
        MsgBox "処理モードを選択してください。", vbExclamation
        Me.cboMode.SetFocus
        ValidateInput = False
        Exit Function
    End If
    
    ValidateInput = True
End Function
VB

フォームのバリデーションで大事なのは、「ユーザーにどこを直せばいいかを即座に伝える」ことです。

エラーメッセージは具体的に
SetFocus でフォーカスを戻す
必要なら、エラー箇所を色付けする

このあたりを“型”として持っておくと、どのフォームでも同じ操作感になります。


重要ポイントの深掘り:バリデーション総合テンプレを“本当に使える武器”にする

1:バリデーションは「本処理の前に一気にやる」

処理の途中で「やっぱりこの値おかしいな」と気づくより、
最初に「入力」「設定」「環境」を一気にチェックした方が、ユーザーにも開発者にも優しいです。

入口でまとめてチェックする
エラーがあれば、その場で止めてメッセージ
通ったら、あとは“信じて”本処理を流す

この構造にすると、本処理のコードから「細かい防御コード」をかなり減らせます。

2:チェック内容を“関数名”で表現する

ValidateCustomerSheet
ValidateJoinRules
ValidateConfigBatch
ValidateFormMain

このように、「何を検査しているか」が関数名で分かるようにしておくと、
コードを読むときに「ここで何を守っているのか」が一瞬で伝わります。

逆に、If文をその場で書き散らすと、「何を守りたかったのか」がすぐに分からなくなります。

3:エラーメッセージは“未来の自分”へのメモだと思って書く

バリデーションのメッセージは、単なるユーザー向けだけではありません。
半年後の自分や、別の人が見たときに「何がダメで止まったのか」を理解するための手がかりです。

どのシートの
どのセル(行・列)で
何がどうおかしいのか

ここまで入れておくと、トラブル対応のスピードが段違いになります。


まとめ:バリデーション総合テンプレは「全部の処理の前に置く関所」

今日のテンプレを一言でまとめると、こうなります。

バリデーション専用モジュール(ModValidation)を用意し、
入力シート・設定シート・フォームごとに Validate○○ 関数を作る。
各処理の入口で「Validate○○がTrueなら本処理へ、Falseなら即終了」という型を徹底する。

これを一度仕組みとして作ってしまえば、
「とりあえず動かしてみて、落ちたら原因を探す」という世界から、
「動かす前におかしなところを教えてくれる」世界に変わります。

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