凡庸なコードを葬り去れ: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システムは、スパゲッティコードの迷宮から脱却し、堅牢なエンタープライズ・アーキテクチャへと進化するだろう。
健闘を祈る。
