Access VBAの「SetFocus地獄」を終わらせる:堅牢なUI制御の極意
Accessで業務アプリを組んでいると、必ず一度は遭遇する壁がある。それが「実行時エラー 2110:このコントロールにフォーカスを移動することはできません」だ。
「なぜ動くはずのコードが落ちるのか?」と悩む時間は、エンジニアにとって最も生産性の低い浪費だ。非表示のコントロール、使用不可(Enabled=False)のフィールド、あるいはフォームが閉じられた後のイベント発火……。これらを場当たり的に `On Error Resume Next` で誤魔化すのは、自ら時限爆弾を埋め込んでいるに等しい。
真のアーキテクトは、エラーを握りつぶすのではなく、「安全に移動できる状態か」を確約する設計を好む。今回は、あなたのAccessアプリを一段上の安定感へ導く、極限のフォーカス制御術を伝授する。
—
なぜ「直書き」が失敗を招くのか
多くの初心者は、フォームのイベント内に直接 `Me.txt_Input.SetFocus` と書いてしまう。しかし、Accessのフォームは動的なオブジェクトだ。
- 状態の変化: 権限やロジックによって `Enabled` や `Visible` がリアルタイムに切り替わる。
- イベントの競合: フォーカスが未確定の状態で別の処理が割り込む。
これを回避するには、「SetFocusの実行権限」を抽象化し、関数に委譲するのが鉄則だ。
—
堅牢性を担保する:SafeSetFocus関数
以下は、私がプロジェクトの共通モジュールで必ず定義する「安全なフォーカス移動関数」だ。単なるエラー回避ではなく、「移動が可能か」を判定してからアクションを起こす、というガード節の思想に基づいている。
‘ —————————————————————————–
‘ 機能:指定したコントロールに安全にフォーカスを移動する
‘ 引数:ctrl – 移動対象のコントロールオブジェクト
‘ 戻り値:成功した場合はTrue、失敗した場合はFalse
‘ —————————————————————————–
Public Function SafeSetFocus(ByRef ctrl As Control) As Boolean
On Error GoTo Err_Handler
‘ 1. オブジェクトが有効かチェック
If ctrl Is Nothing Then Exit Function
‘ 2. 物理的にフォーカスを受け取れる状態かを確認
‘ Visible = False または Enabled = False の場合はスキップ
If ctrl.Visible And ctrl.Enabled Then
ctrl.SetFocus
SafeSetFocus = True
End If
Exit Function
Err_Handler:
‘ 予期せぬエラー(フォームが閉じた直後など)も握りつぶさずログに出力すべきだが、
‘ アプリを止めないことを優先し、ここではFalseを返して制御を戻す
Debug.Print “Focus Error: ” & Err.Description
SafeSetFocus = False
End Function
この設計の肝
- `ByRef Control` の採用: フォーム固有のコントロールを汎用的に受け取る。これにより、コードの再利用性が飛躍的に向上する。
- ガード節による判定: 「移動できる状態か?」を事前にチェックする。`Visible` と `Enabled` を個別に確認することで、エラーの発生源を物理的に遮断している。
—
実務での活用シーン:マスタ登録画面の例
例えば、保存ボタンを押した後に「入力欄の先頭に戻したい」という要件があった場合、以下のように記述する。
Private Sub btn_Save_Click()
‘ データ更新処理(省略)
‘ 先頭の入力項目へ安全に戻る
If Not SafeSetFocus(Me.txt_ItemCode) Then
‘ 必要に応じて、フォーカスを移動できなかった場合の警告をここに書く
MsgBox “入力項目が非表示のため、フォーカスを移動できませんでした。”, vbExclamation
End If
End Sub
—
プロフェッショナルへの道:さらなる高みへ
このコードを使いこなす段階に至ったなら、次の視点を持ってほしい。
1. データベース連携とUIの関係:
`CurrentDb` での直接的なデータ更新とUI上のフォーカス移動を混同してはならない。データ更新処理はビジネスロジック層に分離し、UIの制御はプレゼンテーション層として分離する。これにより、コードの保守性は劇的に向上する。
2. イベントの連鎖を断つ:
`AfterUpdate` イベントで `SetFocus` を使うのは、無限ループの温床だ。どうしても必要な場合は、`Screen.ActiveControl` を利用して動的に判断するか、あるいはUI設計そのものを見直すべきだ。
結びに代えて
Accessは古臭いツールではない。オブジェクトモデルを正しく理解し、そのライフサイクルを制御下に置くことができれば、これほど高速に業務ツールを構築できる環境は他にない。
「動けばいい」コードから「壊れない」コードへ。あなたが書くその1行が、現場の運用担当者のストレスを確実に減らす。その責任と誇りを持って、設計を突き詰めてほしい。
質問があればいつでも歓迎する。だが、まずはこの `SafeSetFocus` を自分のツールに組み込み、エラーログから「2110」の文字を消し去ることから始めてくれ。
