【実務・中級編】【中級】フォームの「ActiveControl」プロパティを活用した、汎用的な入力バリデーション関数の構築 – Access VBA解析バイブル

スポンサーリンク

【中級】ActiveControlが分かればAccess開発は加速する:フォーム入力を完全統御する汎用バリデーション設計

開発現場でこんなコードを目にして、頭を抱えたことはないだろうか。

‘ ❌ やってはいけない「コントロール個別チェック」の悪夢
Private Sub btnSave_Click()
If IsNull(Me.txtCustomerName) Or Me.txtCustomerName = “” Then
MsgBox “顧客名を入力してください。”, vbCritical
Me.txtCustomerName.SetFocus
Exit Sub
End If

If IsNull(Me.txtPostalCode) Or Not IsNumeric(Me.txtPostalCode) Then
MsgBox “郵便番号は数値で入力してください。”, vbCritical
Me.txtPostalCode.SetFocus
Exit Sub
End If

‘ これが数十フィールド続く…
End Sub

フォームに配置されたコントロールの数だけ `If` 文を並べ、エラーが出るたびに `SetFocus` を叩く。新人が入るたびにこのスパゲッティコードが量産され、保守フェーズに入った途端に誰も触れなくなる。これが、Access開発が「レガシー」と揶揄される最大の原因だ。

プロのエンジニアなら、この悪習をここで断ち切ろう。
今回は、フォームの `ActiveControl` プロパティTag(タグ)プロパティ を巧みに組み合わせ、入力バリデーションをたった1つの共通関数に集約する「極限のコード削減術」を伝授する。

1. なぜ「個別実装」は破綻するのか?

コントロールの数だけイベントプロシージャを書く、あるいは保存ボタンのクリックイベントに直書きするアプローチは、以下の重大なтехнический долг(技術的負債)を生む。

1. 仕様変更のコストが倍増する
「このテキストボックスは未入力でもOKになった」「文字数制限が変更された」という要件変更のたびに、巨万のコードの海から該当箇所を探し出し、修正し、デバッグする羽目になる。
2. バリデーションのルールの統一性(一貫性)が失われる
ある画面では「半角数字のみ」、別の画面では「全角許容」といったバラツキが生まれ、ユーザービリティが著しく低下する。

解決の鍵:ActiveControl と Tag プロパティの融合

Accessのフォームには、現在ユーザーがフォーカスを持っている、あるいは直前に操作していたコントロールを指し示す `Me.ActiveControl` という強力なオブジェクトが存在する。

これを利用し、「保存ボタンが押されたとき、フォーム上の全コントロールを走査し、Tagに仕込まれたルールに基づいて動的に検証する」というアーキテクチャを構築する。これが本記事の主題である。

2. 設計思想:タグ駆動型バリデーション(Tag-Driven Validation)

実装に入る前に、設計の全体像を共有する。

1. 各コントロールの `Tag` プロパティに、バリデーションルールを表すキーワード(例: `REQ` = 必須, `NUM` = 数値など)を埋め込む。
2. 共通バリデーション関数を用意し、引数として渡されたコントロールの値を検証する。
3. エラー検知時は、自動的にそのコントロールへフォーカスを当て、背景色を赤く変えて(視覚的フィードバック)、即座に処理を中断する。

これらを完全にカプセル化する。

3. プロダクションコード実装

以下のコードを、標準モジュール(例:`modValidation`)およびフォームのモジュールにそのまま貼り付けて、その圧倒的なスマートさを体感してほしい。

① 標準モジュール(バリデーションエンジン)

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ 汎用バリデーションエンジン

‘ 【Tagプロパティの仕様】
‘ – REQ : 必須入力 (Required)
‘ – NUM : 数値のみ (Numeric)
‘ – 複数の組み合わせは半角スペース区切り (例: “REQ NUM”)
‘ ==============================================================================
Public Function ValidateControl(ByRef ctl As Control) As Boolean
On Error GoTo ErrorHandler

Dim vTag As Variant
Dim i As Integer
Dim varVal As Variant

‘ 検証対象の値を安全に取得
varVal = ctl.Value

‘ Tagが設定されていない場合は検証スキップ
If Trim(Nz(ctl.Tag, “”)) = “” Then
ValidateControl = True
Exit Function
End If

‘ タグをスペースで分解(複合条件に対応)
vTag = Split(Trim(ctl.Tag), ” “)

For i = LBound(vTag) To UBound(vTag)
Select Case UCase(vTag(i))

‘ — 必須チェック —
Case “REQ”
If IsNull(varVal) Or Trim(CStr(varVal)) = “” Then
Call ShowValidationError(ctl, “この項目は入力必須です。”)
Exit Function
End If

‘ — 数値チェック —
Case “NUM”
If Not IsNull(varVal) And Trim(CStr(varVal)) <> “” Then
If Not IsNumeric(varVal) Then
Call ShowValidationError(ctl, “半角数字で入力してください。”)
Exit Function
End If
End If

‘ 必要に応じて “MAIL”, “DATE” などを拡張可能

End Select
Next i

‘ 検証OKの場合、スタイルを元に戻す
Call ResetControlStyle(ctl)
ValidateControl = True
Exit Function

ErrorHandler:
MsgBox “バリデーション実行中に予期せぬエラーが発生しました。” & vbCrLf & _
“Error: ” & Err.Description, vbCritical, “システムエラー”
ValidateControl = False
End Function

‘ ==============================================================================
‘ エラー時の共通UIフィードバック
‘ ==============================================================================
Private Sub ShowValidationError(ByRef ctl As Control, ByVal message As String)
‘ ユーザーフレンドリーな警告表示
MsgBox message, vbExclamation, “入力エラー”

‘ フォーカス移動(非表示やロック状態の考慮)
On Error Resume Next
ctl.SetFocus

‘ 視覚的アピール(背景色を薄い赤に、文字色を黒に)
ctl.BackColor = RGB(255, 220, 220)
On Error GoTo 0
End Sub

‘ ==============================================================================
‘ スタイルリセット
‘ ==============================================================================
Public Sub ResetControlStyle(ByRef ctl As Control)
On Error Resume Next
‘ 標準的な白背景に戻す(テーマやデザインに合わせて調整)
ctl.BackColor = RGB(255, 255, 255)
On Error GoTo 0
End Sub

② フォームモジュール(驚異的にクリーンになった保存処理)

Option Compare Database
Option Explicit

Private Sub btnSave_Click()
Dim ctl As Control

‘ フォーム上の全コントロールをスキャン
For Each ctl In Me.Controls
‘ テキストボックス、コンボボックス等、値を持つコントロールのみ対象
Select Case ctl.ControlType
Case acTextBox, acComboBox, acListBox
‘ 共通関数に判定を委譲
If Not ValidateControl(ctl) Then
‘ エラー時は即座に処理中断(バリデーション内でフォーカス設定済み)
Exit Sub
End If
End Select
Next ctl

‘ すべてのチェックを通過した場合の処理
If MsgBox(“入力内容を保存しますか?”, vbQuestion + vbYesNo, “確認”) = vbYes Then
‘ ここに実際のデータ更新処理(CurrentDb等を用いたSQL実行)を記述
DoCmd.RunCommand acCmdSaveRecord
MsgBox “保存しました。”, vbInformation, “完了”
End If
End Sub

4. チーフアーキテクトからの実践的アドバイス

この実装を現場に導入するにあたり、プロとして押さえておくべきポイントをいくつか共有する。

1. タグの仕様はチーム内で厳格にドキュメント化せよ

`Tag` プロパティは「自由記述ができる」という特性ゆえに、開発者が勝手に `REQ` ではなく `必須` などと書き始めるとシステムが破綻する。チーム内で「大文字の半角スペース区切り」というルールを徹底し、共通定数化(Enumや定数モジュール)するのも一つの手だ。

2. トランザクションとデータベース連携の作法

保存ボタンのイベント内で一連のバリデーションを突破した後、実際のデータベースへの書き込みには `CurrentDb.Execute` やトランザクション制御(`DBEngine.BeginTrans`)を組み合わせるべきだ。
フォームの自動バインド(レコードソース直結)に頼るのではなく、堅牢なDAO/ADOによるSQL構築を行うことで、マルチユーザー環境での競合や整合性エラーを完全にコントロール下における。

3. オブジェクトのライフサイクルとメモリ管理

`For Each ctl In Me.Controls` で取得したコントロール変数は、プロシージャの終了とともに自動解放されるが、参照を保持し続けるようなグローバル変数等に代入してはならない。メモリリークの温床になるため、ローカルスコープで完結させること。

まとめ

今回の手法を導入すれば、今後どれだけフォームの項目が増えようとも、保存ボタンのコードを書き換える必要は一切なくなる。新しい項目を追加したら、プロパティシートの `Tag` に `REQ` と書き込むだけだ。

コードを書く量を減らし、バグが入り込む余地を物理的に排除する。これこそが、モダンなAccess開発の姿である。
あなたのプロジェクトにも今すぐこの設計を取り入れ、レガシーな開発スタイルから脱却してほしい。

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