【テクニカル・上級編】フォームの「Control.SetFocus」エラーを回避する、安全なフォーカス移動の設計 – Access VBA解析バイブル

スポンサーリンク

Access VBAの暗黒面:SetFocusエラーを「制御」し、堅牢なUIを構築する極限の設計論

Accessという環境は、長年現場を支えてきた「老練な戦士」であると同時に、扱いを誤れば即座に実行時エラーという名の地雷を踏む繊細なプラットフォームだ。

特に、UI操作の要である`Control.SetFocus`。多くの初学者はこれを単純なメソッドと見なすが、中堅以上のエンジニアであれば、これがどれほど「不安定な足場」であるかを知っているはずだ。非表示(Visible = False)、使用不可(Enabled = False)、あるいは親フォームがフォーカスを持っていない状態――これらの条件下で`SetFocus`を叩けば、システムは容赦なく停止する。

今日は、場当たり的な`On Error Resume Next`で誤魔化すような無様なコードとは決別し、オブジェクトの状態を厳密に管理する「プロフェッショナルな実装」を伝授する。

1. なぜ「SetFocus」は失敗するのか

Accessのオブジェクトモデルにおいて、フォーカスとは「単一のUI資源」を占有する排他制御だ。以下の条件を一つでも満たさないコントロールにフォーカスを強制しようとすれば、エラー番号2110や2164がシステムを襲う。

  • Visible = True であること
  • Enabled = True であること
  • 親フォームがアクティブ であること

これらを逐一`If`文で判定するのはナンセンスだ。我々が求めるのは、いかなる文脈でも安全にフォーカスを奪取する「抽象化されたガード」である。

2. 堅牢性を極めた「SafeSetFocus」の実装

以下に、オブジェクトの状態を再帰的にチェックし、安全にフォーカスを移動させるための汎用関数を提示する。

‘ @brief 安全にフォーカスを移動する汎用プロシージャ
‘ @param ctrl 対象のコントロールオブジェクト
‘ @return 成功時はTrue、失敗時はFalse
Public Function SafeSetFocus(ByRef ctrl As Object) As Boolean
‘ オブジェクトが有効か、インターフェースを保持しているかを確認
If ctrl Is Nothing Then Exit Function

On Error GoTo Err_Handler

‘ 1. 親フォームのアクティブ化(これが漏れるとSetFocusは機能しない)
If Not ctrl.Parent.Form.ActiveControl Is Nothing Then
If ctrl.Parent.Name <> Screen.ActiveForm.Name Then
DoCmd.SelectObject acForm, ctrl.Parent.Name
End If
End If

‘ 2. 可視性および使用可否の厳密なチェック
‘ プロパティが存在しないオブジェクトへのアクセスはエラーになるため、On Errorで逃げる
If ctrl.Visible And ctrl.Enabled Then
ctrl.SetFocus
SafeSetFocus = True
End If

Exit Function

Err_Handler:
‘ ログ出力やトレースが必要な場合はここに記述
SafeSetFocus = False
End Function

3. シニアエンジニアが意識すべき「メモリとパフォーマンス」の視点

上記コードをさらに洗練させるためには、以下の「アーキテクトの視点」を忘れてはならない。

オブジェクトの明示的解放(Nothingへの帰還)

VBAのガベージコレクションを信用してはならない。特にAccessのモジュール間でオブジェクトを参照渡しする場合、循環参照がメモリリークを引き起こす。`Set obj = Nothing` を徹底することは、長期稼働する業務システムにおいて「メモリの断片化」を防ぐ唯一の手段だ。

APIによる低レイヤー制御の是非

もし、どうしてもフォーカスが制御できない「難解なActiveXコントロール」に遭遇した場合、Windows API(`SetFocus`関数など)を直接叩く手法もある。しかし、それはAccessのイベントループ(`GotFocus`/`LostFocus`)を破壊する危険がある。
「APIで解決するのは、Accessの標準機能が完全に論理破綻した時のみ」というのが、私の長年の教訓だ。

4. 保守性を高めるための設計思想

システム管理者が最も恐れるのは、仕様変更時に「どこでフォーカスが制御されているか」がブラックボックス化することだ。

  • 集中管理: `SetFocus`をコードのあちこちに散らばらせるな。専用のモジュール(例: `modUIUtility`)に集約し、すべてのUI遷移をこの関数経由で行うよう強制せよ。
  • エラーの可視化: 本番環境では`Resume Next`で隠蔽しても良いが、開発環境では`Debug.Assert`を使用して、フォーカス移動に失敗した瞬間にIDEを停止させる設計にすべきだ。

結論:コードは「対話」である

Access VBAはレガシーと言われるが、それは「制御の難しさ」を理解していない者が放つ言葉だ。オブジェクトのライフサイクルを理解し、状態遷移を予測してコードを書くことは、まさにシステムと対話することに他ならない。

今回提示した`SafeSetFocus`は、君たちの書くシステムから「予期せぬ実行時エラー」を排除する強力な武器となるはずだ。

明日からの開発において、`SetFocus`を呼ぶとき、一度立ち止まって考えてみてほしい。「このコントロールは、本当にフォーカスを受ける準備ができているか?」と。その問いこそが、真のエンジニアへの入り口だ。

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