Accessで「レスポンシブUI」を実装する:フォームリサイズ制御の深淵
Accessは、しばしば「レガシーの墓場」と揶揄される。しかし、それは扱う者の習熟度不足に過ぎない。適切にオブジェクトモデルを掌握し、Windows APIを直下に叩き込めば、Accessはモダンな業務アプリケーションの基盤として十分なポテンシャルを発揮する。
今回は、最も要望が多いにもかかわらず、多くのエンジニアが「泥臭い力技」で実装してメモリを食いつぶしている「フォームリサイズ時のコントロール動的最適化」について、アーキテクトの視点から解法を提示する。
—
1. `OnResize`イベントの罠と最適化の哲学
Accessの`Form_Resize`イベントは、ウィンドウサイズ変更時に極めて頻繁に発火する。ここで安易に`CurrentDb`を開いたり、重い再描画処理を走らせることは、UIスレッドのスタックを圧迫し、アプリケーションの応答性を著しく低下させる。
極限の最適化の指針:
- レイアウト計算はメモリ上で完結させる: `Control.Left`や`Control.Width`を直接変更する前に、計算式は変数に格納し、描画反映は一括で行う。
- Echoメソッドの封印と解放: `Application.Echo False`で描画を停止させ、全計算終了後に`True`に戻すのは定石だが、エラーハンドリングを怠るとAccessがフリーズしたまま復帰不能になるリスクがある。必ず`Finally`相当の処理を担保せよ。
- 不要なオブジェクト生成を避ける: ループ内での`Me.Controls`へのアクセスは極力控え、一度参照をメモリにキャッシュする。
—
2. 実装:プロフェッショナル・リサイズ・エンジン
以下に、コントロールの相対配置を管理するクラスモジュール的アプローチのコードを示す。これを標準モジュールに置くのではなく、各フォームのモジュールに組み込むことで、カプセル化とパフォーマンスを両立させる。
‘ フォームモジュール内に実装
Option Compare Database
Option Explicit
‘ コントロールの初期位置を保存する構造体
Private Type ControlLayout
Name As String
Left As Long
Top As Long
Width As Long
Height As Long
End Type
Private m_OriginalWidth As Long
Private m_OriginalHeight As Long
Private m_Layouts() As ControlLayout
Private Sub Form_Open(Cancel As Integer)
‘ 初期状態の保存(メモリ最適化のために配列へ展開)
Dim c As Control
Dim i As Long
m_OriginalWidth = Me.InsideWidth
m_OriginalHeight = Me.InsideHeight
ReDim m_Layouts(0 To Me.Controls.Count – 1)
For Each c In Me.Controls
‘ リサイズ対象外のコントロールはTagプロパティで判定
If c.Tag <> “Fixed” Then
With m_Layouts(i)
.Name = c.Name
.Left = c.Left
.Top = c.Top
.Width = c.Width
.Height = c.Height
End With
End If
i = i + 1
Next c
End Sub
Private Sub Form_Resize()
‘ 描画停止によるチラつき防止とパフォーマンス向上
On Error Resume Next
Application.Echo False
Dim c As Control
Dim ratioW As Double, ratioH As Double
Dim i As Long
ratioW = Me.InsideWidth / m_OriginalWidth
ratioH = Me.InsideHeight / m_OriginalHeight
For i = LBound(m_Layouts) To UBound(m_Layouts)
Set c = Me.Controls(m_Layouts(i).Name)
With c
.Left = m_Layouts(i).Left ratioW
.Top = m_Layouts(i).Top ratioH
.Width = m_Layouts(i).Width ratioW
.Height = m_Layouts(i).Height ratioH
End With
Next i
Application.Echo True
End Sub
—
3. APIによる更なる制御:DPI対応とウィンドウメッセージ
高解像度ディスプレイ(4Kモニタ)環境において、Accessの座標系はしばしば狂いを生じる。`Twips`単位の計算は標準的だが、Windows APIの`GetDeviceCaps`を呼び出し、論理DPIを考慮したスケーリングを実装するのが、真に堅牢なシステム管理者の流儀だ。
もし、フォームのリサイズだけでなく、ウィンドウ自体の最小化・最大化イベントをより精密に制御したい場合は、`WindowProc`をフックして`WM_SIZE`メッセージを拾う必要がある。これはVBAの領域を超えたように見えるが、`AddressOf`演算子を使えばAccess内でも実装可能だ。
4. アーキテクトからの提言:継続的メンテナンスのために
このコードを実装する際、以下のルールをチームに徹底してほしい。
1. Tagプロパティの活用: `Fixed`という文字列をタグに入れるだけで、リサイズ対象外とするルールを徹底する。これにより、レイアウトの柔軟性を担保する。
2. イベントの連鎖を断つ: リサイズ処理中に他のイベントが発火しないよう、`Application.Echo`だけでなく、フラグ変数(`m_IsResizing`)を用いて再帰呼び出しを防止せよ。
3. オブジェクトの解放: `Set c = Nothing` をループの最後で行うことは、メモリ管理の観点からは現代のVBAでは必須ではないが、大規模なフォームでは「意図的に参照を捨てる」習慣がメモリリークを防ぐ。
Accessというプラットフォームは、決して「古い」のではない。使い手のアーキテクチャ設計が「古い」だけだ。このリサイズエンジンを基点に、モダンなUI体験をユーザーに提供してほしい。それが、現場を知るエンジニアの矜持である。
