【テクニカル・上級編】フォームのOnResizeイベントでコントロール配置を動的に最適化するレスポンシブUI – Access VBA解析バイブル

スポンサーリンク

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体験をユーザーに提供してほしい。それが、現場を知るエンジニアの矜持である。

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