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

スポンサーリンク

Access VBAを掌握する極限の知見:OnResizeイベントによる極限のレスポンシブUI構築

レガシーシステムの代名詞のように語られるMicrosoft Accessだが、業務システムの現場において、その圧倒的な開発スピードとデータハンドリング能力はいまだに代替不可能な価値を持つ。

しかし、現代の多様な解像度を持つディスプレイ環境において、固定レイアウトのAccessフォームは致命的なユーザビリティの低下を招く。全画面表示にした途端、右側に広大なデッドスペースが生まれ、肝心のサブフォームやリストビューは小さな小窓のまま――。この悪夢のようなUIを打破するため、多くの開発者が頭を悩ませてきた。

今回は、Accessフォームの`OnResize`イベントを完全に手懐け、Windows APIの知識とオブジェクトライフサイクルの最適化を駆使して、「チラつきのない高速な動的レイアウト制御(レスポンシブUI)」を実現する極限のテクニックを授ける。

1. AccessにおけるUI制御のパラダイムシフト

Access標準のレイアウト機能(「積み上げ」や「表形式」など)は、単純な帳票フォームであれば有用だが、複雑な業務要件を持つマスタ・トランザクション画面や、多数のタブ・グリッドが混在するダッシュボードにおいては完全に力不足に陥る。

ここで必要となるのが、ウィンドウサイズ変更時に発火する`OnResize`イベントをフックし、コードベースでコントロールの座標(`Left`, `Top`)とサイズ(`Width`, `Height`)をミリ秒単位で再計算するアプローチである。

だが、素朴なVBAコードでこれを実装すると、「画面の再描画による激しいチラつき(フリッカー)」「メモリリークやパフォーマンス低下」という、Access開発者が最も恐れる魔物が牙を剥く。

この極限状態を突破するため、以下の3つの要件を満たすアーキテクチャを構築する。

1. 描画抑制(LockWindowUpdate API)によるフリッカーの完全排除
2. マジックナンバーを排した相対座標計算ロジック
3. CurrentDbとオブジェクトの適切なスコープ管理によるメモリ最適化

2. 実装コード:動的リサイズエンジンの全貌

以下のコードは、単なるサンプルではない。実務の巨大な業務システムにそのまま組み込める、プロダクションクオリティのフォームモジュールである。

対象フォームのモジュールに、以下のコードを記述せよ。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ フォーム動的リサイズ制御モジュール
‘ アーキテクチャ設計: チーフアーキテクト
‘ ==============================================================================

‘ ウィンドウの再描画をロックし、リサイズ時のチラつきを完全に消し去るためのWindows API
If VBA7 Then
Private Declare PtrSafe Function LockWindowUpdate Lib “user32” (ByVal hwndLock As LongPtr) As Long
Else
Private Declare Function LockWindowUpdate Lib “user32” (ByVal hwndLock As Long) As Long
End If

‘ フォーム初期化時の基準サイズ(デザイン時のクライアント領域サイズを保持)
Private m_InitialFormWidth As Long
Private m_InitialFormHeight As Long

‘ コントロールの初期相対位置を保持するUDF(ユーザー定義型)
Private Type ControlLayout
Name As String
OriginalLeft As Long
OriginalTop As Long
OriginalWidth As Long
OriginalHeight As Long
End Type

Private m_Layouts() As ControlLayout
Private m_IsInitialized As Boolean

Private Sub Form_Open(Cancel As Integer)
‘ ライフサイクルの初期化
m_IsInitialized = False
End Sub

Private Sub Form_Load()
Dim ctl As Control
Dim cnt As Long

‘ フォームのデザイン時(基準)サイズを記憶
‘ ※Twip単位での正確な保持が肝要
m_InitialFormWidth = Me.InsideWidth
m_InitialFormHeight = Me.InsideHeight

‘ 配置されているコントロールの数をカウント
cnt = 0
For Each ctl In Me.Controls
‘ タブコントロールやラベル、テキストボックス等を対象とする
‘ (※必要に応じて隠しコントロールや特定のプレフィックスを除外すること)
Select Case ctl.ControlType
Case acTextBox, acComboBox, acListBox, acCommandButton, acSubform, acTabCtl, acLabel
cnt = cnt + 1
End Select
Next ctl

If cnt = 0 Then Exit Sub

‘ 構造体配列の動的確保
ReDim m_Layouts(1 To cnt)

Dim i As Long
i = 1
For Each ctl In Me.Controls
Select Case ctl.ControlType
Case acTextBox, acComboBox, acListBox, acCommandButton, acSubform, acTabCtl, acLabel
With m_Layouts(i)
.Name = ctl.Name
.OriginalLeft = ctl.Left
.OriginalTop = ctl.Top
.OriginalWidth = ctl.Width
.OriginalHeight = ctl.Height
End With
i = i + 1
End Select
Next ctl

m_IsInitialized = True
End Sub

Private Sub Form_Resize()
‘ 初期化が完了していない場合や、最小化されている場合は処理をスキップ
If Not m_IsInitialized Then Exit Sub
If Me.InsideWidth = 0 Or Me.InsideHeight = 0 Then Exit Sub

Dim currentWidth As Long
Dim currentHeight As Long
Dim widthRatio As Double
Dim heightRatio As Double
Dim i As Long
Dim targetCtl As Control

currentWidth = Me.InsideWidth
currentHeight = Me.InsideHeight

‘ 拡大・縮小率の算出(基準サイズに対する現在のサイズの比率)
widthRatio = currentWidth / m_InitialFormWidth
heightRatio = currentHeight / m_InitialFormHeight

‘ 【極限最適化】APIを用いて描画をロックし、コントロール移動時の画面の波打ちを防ぐ
On Error Resume Next
LockWindowUpdate Me.Hwnd

‘ 配列にキャッシュした初期値をもとに、全コントロールの座標を高速再計算
For i = LBound(m_Layouts) To UBound(m_Layouts)
Set targetCtl = Me.Controls(m_Layouts(i).Name)

If Not targetCtl Is Nothing Then
‘ コントロール種別に応じたスケーリング戦略
‘ 例:右端に追従すべきもの、高さだけが変わるべきものをここで制御可能

‘ 標準的な全方向追従ロジック(必要に応じてX軸・Y軸の倍率を個別に調整)
targetCtl.Left = m_Layouts(i).OriginalLeft widthRatio
targetCtl.Top = m_Layouts(i).OriginalTop heightRatio

‘ グリッドやサブフォーム、タブコントロールは幅と高さも動的に拡縮
Select Case targetCtl.ControlType
Case acSubform, acListBox, acTabCtl, acTextBox
‘ 例として、テキストボックスは幅のみ追従、サブフォームは全面追従など分岐可能
If targetCtl.ControlType = acTextBox Then
‘ テキストボックスは横幅のみ引き伸ばす場合
targetCtl.Width = m_Layouts(i).OriginalWidth widthRatio
Else
‘ サブフォームやタブは縦横両方に追従
targetCtl.Width = m_Layouts(i).OriginalWidth widthRatio
targetCtl.Height = m_Layouts(i).OriginalHeight heightRatio
End If
End Select
End If
Next i

CleanUp:
‘ 描画ロックの確実に解除(これを怠るとデスクトップ全体の描画がフリーズする)
LockWindowUpdate 0
On Error GoTo 0
End Sub

3. チーフアーキテクトが解説する「極限の知見」

上記のコードには、長年のレガシーシステム保守とパフォーマンスチューニングの現場で培った、妥協なきエンジニアリングの哲学が組み込まれている。

① `LockWindowUpdate` によるフリッカーレス化

Accessのフォーム上で複数のコントロールの `Left` や `Top` をループ内で書き換えると、OSはコントロールが移動する都度(あるいは数個おきに)画面を再描画するため、激しい画面の「チラつき(フリッカー)」が発生する。
これを防ぐため、リサイズ処理の直前でWindows APIの `LockWindowUpdate` を呼び出し、ウィンドウのハンドル(`Me.Hwnd`)を指定して描画を強制停止している。処理の最後で `0` を渡してロックを必ず解除する(`CleanUp` ラベルの徹底)ことで、メモリリークや描画不全のリスクを完全に排除している。

② コントロール情報の事前キャッシュ(構造体配列の活用)

多くの開発者がやりがちなアンチパターンとして、`Form_Resize` イベントの内部で毎回 `Me.Controls` コレクションを走査し、デザイン時のプロパティを参照しようとすることが挙げられる。
Accessのオブジェクトモデルへの頻繁なアクセスはVBAランタイムにとって重いオーバーヘッドであり、リサイズ中のような高頻度で発火するイベント内で行うと、致命的なカクつきの原因になる。
本実装では、`Form_Load` 時に一度だけ全コントロールの初期座標をUDF(ユーザー定義型)のメモリ上の配列 `m_Layouts()` にキャッシュし、`Form_Resize` ではメモリ上の数値計算のみで完結させている。この設計思想が、圧倒的なレスポンス速度を生み出す。

③ Twip単位の厳密なスケーリング

Accessのレイアウト座標系は「Twip(1 inch = 1440 Twip)」を基準としている。ピクセル単位での計算を行おうとすると、高DPI環境(4Kディスプレイやスケーリング設定が125%、150%の環境)でレイアウトが崩壊する。
`Me.InsideWidth` および `Me.InsideHeight` は常に正確なクライアント領域のTwip値を返すため、これを用いた比率計算(`widthRatio` / `heightRatio`)は、環境依存のない極めて堅牢なスケーリングを実現する。

4. 現場での運用とシステム間連携における注意点

このレスポンシブUIエンジンを導入するにあたり、シニアエンジinerとして以下の運用統制を推奨する。

  • 最小サイズの制限(Minimum Size Enforcement)

フォームが極端に縮小された場合、コントロール同士が重なり合って視認性が失われる。`Form_Resize` 内で `If currentWidth < 6000 Then currentWidth = 6000` のようなガード節を設け、フォーム自体の最小サイズを下回らない制御を組み合わせると完璧である。

  • サブフォームとの連携

メインフォーム上のサブフォームがリサイズされる際、サブフォーム内の子フォーム側でも同様に `Form_Resize` を発火させたい場合がある。その場合は、サブフォームコントロールの `Form.Requery` ではなく、サブフォーム側のVBAモジュールに連動したリサイズロジックをカプセル化し、メインから伝播させる設計をとるとスマートである。

総括

Accessは「古い技術」ではない。「使いこなせていない設計」があるだけだ。

今回紹介した `OnResize` イベントのフックとAPIによる描画制御、そしてオブジェクトライフサイクルを意識したメモリキャッシュの技法は、Access VBAの限界を押し上げ、モダンなWebアプリケーションに匹敵するUIエクスペリエンスをレガシー環境もたらす。

妥協なきコードを書け。あなたのシステムは、まだもっと速く、美しく動くはずだ。

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