【上級】Accessの「ダイアログ制御」を極める:プロフェッショナルな画面遷移設計
Access開発で避けて通れないのが「呼び出し元と呼び出し先の同期」だ。多くの開発者が `DoCmd.OpenForm` を単に画面を開くコマンドとして使っているが、それはまだAccessのポテンシャルの10%も引き出せていない。
今回は、`acDialog` モードを核とした、「堅牢で、呼び出し元と強固に結合し、かつ再利用性の高い」ダイアログ設計の極意を伝授する。
—
1. なぜ「非同期」なフォーム遷移は破綻するのか
初心者が陥る最大の罠は、フォームを開く際に「処理を待機させない」ことだ。
`DoCmd.OpenForm` をデフォルトで実行すると、呼び出し元(親)のVBAコードは、フォームが開かれた瞬間に次の行へ進んでしまう。これにより、ユーザーが入力した値を親側で取得しようとしても、まだ値が存在しないというタイミングのズレ(Race Condition)が生じる。
これを回避するために、`acDialog` を使う。これは単なる「表示形式」ではない。「呼び出し元のコードの実行を、そのフォームが閉じるか非表示になるまで完全に凍結させる」という、同期制御のための強力なツールだ。
—
2. 堅牢なダイアログ制御の実装パターン
ダイアログの設計で重要なのは、「呼び出し元との密結合を避け、インターフェースを明確にすること」である。フォームに直接値を書き込むのではなく、プロパティや引数を介して制御する。
実装の鉄則:
1. `acDialog` で呼び出す:呼び出し元のコードを一時停止させる。
2. `Visible = False` で閉じる:フォームを閉じて破棄するのではなく、隠すことで「値」を保持したまま呼び出し元に制御を戻す。
3. 呼び出し元で値を回収する:制御が戻った直後に値を抽出し、最後にフォームを `DoCmd.Close` する。
プロダクションコード例
【呼び出し側(親フォーム)のロジック】
Public Sub ShowCustomerSearch()
‘ 1. ダイアログとして開く(呼び出し元はここで待機)
DoCmd.OpenForm “frmCustomerSearch”, WindowMode:=acDialog
‘ 2. フォームが閉じられたか、非表示になった後にここへ到達する
If CurrentProject.AllForms(“frmCustomerSearch”).IsLoaded Then
‘ フォームから値を取得
Dim selectedID As Long
selectedID = Forms(“frmCustomerSearch”).SelectedID
‘ 3. 後処理:フォームを完全に破棄する
DoCmd.Close acForm, “frmCustomerSearch”
‘ 4. 取得した値を使った後続処理
Debug.Print “選択されたID: ” & selectedID
Else
‘ キャンセルされた場合の処理
Debug.Print “検索がキャンセルされました。”
End If
End Sub
【呼び出し先(ダイアログフォーム)のロジック】
‘ フォーム上の「OKボタン」クリック時
Private Sub btnOK_Click()
‘ フォームを閉じずに「非表示」にする(値保持のため)
Me.Visible = False
End Sub
‘ フォーム上の「キャンセルボタン」クリック時
Private Sub btnCancel_Click()
‘ フォームを完全に閉じる(呼び出し元は IsLoaded で判定可能)
DoCmd.Close acForm, Me.Name
End Sub
—
3. なぜこの設計が「プロフェッショナル」なのか
このコードのどこが優れているのか、アーキテクトの視点から解説する。
- ライフサイクル管理: `acDialog` はフォームのライフサイクルを呼び出し元のコンテキストと同期させる。これにより、エラー処理の予測可能性が飛躍的に高まる。
- 例外処理への耐性: ユーザーがフォーム右上の「×」ボタンを押して閉じた場合、`IsLoaded` チェックが機能し、Null参照エラーを未然に防げる。
- メモリ効率: フォームを「閉じて開く」ではなく「非表示にして再利用」することで、特に複雑なサブフォームを持つフォームにおいて、描画負荷を最小限に抑えられる。
—
4. 現場で避けるべきアンチパターン
最後に、現場でよく見る「技術的負債」の例を挙げておく。これらは絶対に避けてほしい。
- グローバル変数への依存: フォーム間で値をやり取りするために `Public` 変数を多用してはいけない。モジュールレベルの変数は「どこで値が書き換わったか」を追跡不能にする。必ずフォームのプロパティ(`Me.Value` のようなインターフェース)を経由させること。
- `DoEvents` での無理やり同期: `DoEvents` をループさせて待機するコードは、CPUリソースの無駄であり、極めて不安定だ。`acDialog` というネイティブな同期機構がある以上、それを使わない理由は存在しない。
—
結論:Accessの設計は「制約」を「武器」にする
Access VBAは古い言語だと言われることもある。しかし、その「同期的なイベント駆動モデル」を深く理解し、`acDialog` のような制御を使いこなせば、モダンなWebアプリにも負けない、堅牢でメンテナンス性の高い業務ツールを構築できる。
コードを書く際は常に自問してほしい。「この処理は、呼び出し元のフローに対して明確な責任を持っているか?」と。
これからの開発において、フォームを開くたびにこの設計を思い出してほしい。その一行のコードが、将来のバグを何百個も防ぐ鍵となるはずだ。
