【テクニカル・上級編】フォームの「ActiveControl」プロパティで実現する、汎用的な入力バリデーション関数 – Access VBA解析バイブル

スポンサーリンク

凡庸なコードを葬り去れ:ActiveControlによるバリデーションの極致

Access開発において、各コントロールの `BeforeUpdate` イベントに個別にバリデーションを記述する時代は終わった。数百のコントロールに散らばる「似たようなチェックロジック」は、保守性の癌であり、メモリの無駄遣いである。

真のアーキテクトは、コントロールのライフサイクルを制御し、一点に集約する。今回は、`Screen.ActiveControl` を駆使した、DRY(Don’t Repeat Yourself)原則に基づく「汎用バリデーションエンジン」の設計思想を伝授する。

1. なぜ「イベント個別記述」が死を招くのか

各コントロールに直接ロジックを書くことは、以下の技術的負債を累積させる。

  • カプセル化の欠如: ビジネスルールがフォームの背後に埋没し、再利用不能になる。
  • メモリ・フットプリントの肥大化: コントロールごとにイベントプロシージャが生成され、フォームのコンパイルサイズが膨張する。
  • 保守性の崩壊: 仕様変更のたびに、数十個のイベントを探し回る惨状。

2. ActiveControlを支配する:集中管理型バリデーション

フォームの `BeforeUpdate` イベントをフックし、`Screen.ActiveControl` を通じてフォーカスがある対象を特定する。これにより、バリデーションロジックを標準モジュールに完全隔離する。

実装:汎用バリデーション・エンジン

‘ 標準モジュール: modValidator
Option Explicit

‘ 汎用バリデーションの実行メソッド
‘ 呼び出し側フォームの BeforeUpdate で実行する
Public Function ValidateActiveControl() As Boolean
Dim ctrl As Control
Set ctrl = Screen.ActiveControl

‘ 型判定を行い、適切な検証ロジックへルーティング
Select Case ctrl.ControlType
Case acTextBox
If Not ValidateTextBox(ctrl) Then Exit Function
Case acComboBox
If Not ValidateComboBox(ctrl) Then Exit Function
End Select

ValidateActiveControl = True
End Function

Private Function ValidateTextBox(ByRef ctrl As Control) As Boolean
‘ 必須チェック
If IsNull(ctrl.Value) Or Trim(ctrl.Value) = “” Then
MsgBox ctrl.Name & ” は必須項目です。”, vbCritical
ctrl.SetFocus
Exit Function
End If

‘ ここにカスタムバリデーション(正規表現API等)を追加可能
ValidateTextBox = True
End Function

3. レガシーの深淵:API連携による「入力制限」

単なるNullチェックでは満足できない諸兄へ。Windows APIを呼び出し、コントロールのメモリレイアウトを直接操作することで、入力マスクの限界を突破する手法がある。

例えば、`SendMessage` APIを用いてコントロールへ直接メッセージを送り、強制的にIME状態を制御したり、入力文字数をバイト単位で厳密に制限することが可能だ。

If VBA7 Then
Private Declare PtrSafe Function SendMessage Lib “user32” Alias “SendMessageA” _
(ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr
Else
Private Declare Function SendMessage Lib “user32” Alias “SendMessageA” _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
End If

‘ コントロールの最大文字数をAPI経由で強制制限する例
Public Sub EnforceMaxLength(ctrl As Control, length As Long)
Const EM_LIMITTEXT = &HC5
SendMessage ctrl.hwnd, EM_LIMITTEXT, length, 0
End Sub

4. チーフアーキテクトの戒め:メモリとオブジェクトのライフサイクル

Access VBAにおいて、`Set` キーワードでオブジェクトを生成した後の `Nothing` 解放は儀式ではない。必須の作法だ。

特に `CurrentDb` を多用するコードでは、以下を徹底せよ。

1. CurrentDbのキャッシュ: `Set db = CurrentDb` とし、再利用せよ。毎回 `CurrentDb` を呼び出すのは、都度オブジェクトを再生成するコストを支払っているに等しい。
2. イベントの連鎖を断つ: バリデーションエラー時は `Cancel = True` を適切に設定し、不要な更新イベントの連鎖を物理的に遮断せよ。

結びに:コードは「機能」ではなく「哲学」である

貴殿が書くその一行が、数年後のメンテナンス担当者の死因にならないよう、常に抽象化のレイヤーを意識せよ。`ActiveControl` は単なるプロパティではない。システムを制御下に置くための、強力なインターフェースだ。

この設計手法を取り入れることで、貴殿のAccessシステムは、スパゲッティコードの迷宮から脱却し、堅牢なエンタープライズ・アーキテクチャへと進化するだろう。

健闘を祈る。

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