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

スポンサーリンク

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

レガシーシステムの代名詞のように語られるMicrosoft Accessだが、その実態は、適切に調教すれば数百万レコードのローカル処理を瞬時に裁き、リッチなクライアントUIを構築できる強力なラピッド開発プラットフォームである。

しかし、現場の開発者から最も多く聞く悲鳴がこれだ。
「フルHDや4Kディスプレイで表示すると、フォームの右側がスカスカになる」
「ウィンドウを引き延ばしても、中のリストボックスが追従せず、操作性が最悪だ」

Access標準の「レイアウトコントロール」は、単純なグリッド配置には使えるが、複雑な業務アプリケーションで求められる「特定の部分だけを可変にし、ボタン群は右下に固定する」といった高度な動的レイアウトには無力である。

今回は、フォームの `OnResize` イベントを完全掌握し、Windows APIの知見とオブジェクトのライフサイクル管理を融合させた、実用耐性100%のレスポンシブUI実装アーキテクチャを提示する。

1. Accessフォームのレイアウト変更における根本的課題

ウィンドウサイズが変更されたとき、Accessはフォームの `Resize` イベントを発火させる。ここで各コントロールの `Left`、ジュース(Top)、`Width`、`Height` を再計算して代入すればよい――理論は単純だ。

しかし、ここに実務の罠がある。

1. 画面のチラつき(フリッカ)の発生
プロパティを1つ変更するたびに再描画(リペイント)走ると、画面が激しくちらつき、ユーザーにストレスを与える。
2. 無駄なイベント連鎖とパフォーマンス劣化
リサイズ中は `Resize` イベントがミリ秒単位で連続発生する。ここで重い演算や不適切なオブジェクト参照を行うと、メモリリークやUIのフリーズを招く。
3. 基準値の喪失
初期ロード時のサイズを基準(ベースライン)とせず、現在のサイズから相対計算を繰り返すと、浮動小数点演算の誤差が蓄積し、コントロールが徐々にズレて消滅する。

これらを解決するためには、「描画の抑制(API制御)」「初期状態のイミュータブル(不変)な保持」「一括プロパティ更新」の3つが不可欠となる。

2. 究極のレスポンシブ制御モジュール実装

以下のコードは、単なる「サイズ変更のコピペコード」ではない。APIによる描画停止、オブジェクトの適切なスコープ管理、そして誤差を排除したスケーリングロジックを実装した、プロダクションクオリティのフォームモジュールである。

このコードを、リサイズを適用したいAccessフォームのクラスモジュールにそのまま実装してほしい。

Option Compare Database
Option Explicit

‘ ==============================================================================
‘ フォームレスポンシブ制御エンジン
‘ 概要: OnResizeイベントをトリガーに、内部コントロール群を動的にスケーリングする
‘ ==============================================================================

‘ Windows API: 描画の凍結・解凍(フリッカ防止の極意)
If VBA7 Then
Private Declare PtrSafe Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As Long) As LongPtr
Else
Private Declare Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
End If

Private Const WM_SETREDRAW As Long = &HB

‘ フォームの初期デザインサイズ(基準値・ツイップ単位)
Private m_BaseWidth As Long
Private m_BaseHeight As Long

‘ 初期化フラグ(ロード完了前のResizeイベント暴走を防ぐ)
Private m_IsInitialized As Boolean

Private Sub Form_Load()
On Error GoTo ErrorHandler

‘ 1. 基準となるデザイン時のフォームサイズを退避
‘ ※デザインビューで保存した時点のサイズが基準となります
m_BaseWidth = Me.InsideWidth
m_BaseHeight = Me.InsideHeight

m_IsInitialized = True
Exit Sub

ErrorHandler:
MsgBox “Form_Load 致命的エラー: ” & Err.Description, vbCritical
End Sub

Private Sub Form_Resize()
‘ 初期化前、または最小化時は処理をスキップ
If Not m_IsInitialized Then Exit Sub
If Me.InsideWidth = 0 Or Me.InsideHeight = 0 Then Exit Sub

On Error GoTo ErrorHandler

‘ 【極意1】描画を完全停止し、フリッカ(画面のちらつき)を根絶する
‘ コントロールを一括変更する前にウィンドウの再描画を止めるのがプロの鉄則
Call SendMessage(Me.hwnd, WM_SETREDRAW, 0, 0)

‘ 2. 現在のウィンドウサイズと基準サイズの倍率を算出
Dim scaleX As Double
Dim scaleY As Double
scaleX = CDbl(Me.InsideWidth) / CDbl(m_BaseWidth)
scaleY = CDbl(Me.InsideHeight) / CDbl(m_BaseHeight)

‘ ==========================================================================
‘ 【極意2】コントロールごとの挙動定義(個別最適化)
‘ ==========================================================================

‘ 例A: 検索条件エリア(上部) -> 幅だけ追従、高さは固定
Call AdjustControl(Me.txtSearchKeyword, , , scaleX, 1)

‘ 例B: メイン一覧リストボックス(中央) -> 幅も高さも完全に追従
Call AdjustControl(Me.lstResults, , , scaleX, scaleY)

‘ 例C: 閉じるボタン・登録ボタン(右下) -> 位置を右下に固定しつつ追従
‘ ※右端からのオフセットを維持するため、特殊な配置計算を行う場合は個別に記述
Call AdjustControl(Me.cmdClose, scaleX, scaleY, 1, 1)
Call AdjustControl(Me.cmdSave, scaleX, scaleY, 1, 1)

CleanUp:
‘ 【極意3】描画の再開と強制リフレッシュ
Call SendMessage(Me.hwnd, WM_SETREDRAW, 1, 0)
Me.Repaint
Exit Sub

ErrorHandler:
‘ エラー時も描画ロックだけは必ず解除する(さもないと画面が真っ白でフリーズする)
Call SendMessage(Me.hwnd, WM_SETREDRAW, 1, 0)
Resume CleanUp
End Sub

‘ ==============================================================================
‘ コントロール位置・サイズ一括調整ヘルパー
‘ ==============================================================================
Private Sub AdjustControl(ctl As Control, Optional ByVal ratioX As Double = 1, Optional ByVal ratioY As Double = 1, Optional ByVal scaleW As Double = 1, Optional ByVal scaleH As Double = 1)
‘ デザイナ上の初期位置を保持するカスタムプロパティはないため、
‘ 初回ロード時の相対位置を維持する設計にするか、あらかじめTagプロパティに
‘ 「初期Left,初期Top,初期Width,初期Height」をカンマ区切りで格納しておくのが王道。
‘ 今回は簡略化のため、Tagプロパティからのパース処理を前提とする。

Dim baseProps() As String
If Len(ctl.Tag) = 0 Then Exit Sub ‘ Tagが未設定ならスキップ

baseProps = Split(ctl.Tag, “,”)
If UBound(baseProps) <> 3 Then Exit Sub

‘ 浮動小数点の誤差を排除するため、CLngで整数(ツイップ)に丸める
On Error Resume Next

‘ 位置の調整(右寄せ・下寄せなどのオフセット考慮)
If ratioX <> 1 Then
ctl.Left = CLng(Val(baseProps(0))) ratioX
End If
If ratioY <> 1 Then
ctl.Top = CLng(Val(baseProps(1))) ratioY
End If

‘ サイズの調整
If scaleW <> 1 Then
ctl.Width = CLng(Val(baseProps(2))) scaleW
End If
If scaleH <> 1 Then
ctl.Height = CLng(Val(baseProps(3))) scaleH
End If

On Error GoTo 0
End Sub

3. チーフアーキテクトが解説する実装の急所

上記のコードが「なぜ現場で生き残るのか」、その技術的根拠を深掘りする。

1. `SendMessage(…, WM_SETREDRAW, 0, 0)` による完全な描画抑制

Access VBAで複数のコントロールの `Width` や `Height` を次々に書き換えると、Windowsはその都度GDI(Graphics Device Interface)を通じて画面を再描画する。これがリサイズ時の「カクつき」や「チラつき」の正体だ。
APIを用いてメッセージキューの描画フラグを `FALSE` に倒すことで、裏側で幾ら座標計算が変わろうとも画面は一切再描画されない。 処理の最後に `1` に戻して `Me.Repaint` を叩くことで、一瞬でレイアウトが確定する。このテクニックの有無で、ユーザー体験は雲泥の差となる。

2. Tagプロパティを活用した「ステートレス」な設計

前述の通り、リサイズイベント内で「現在の幅に1.1を掛ける」という実装をすると、演算誤差が蓄積してコントロールが変形・消失する。
これを防ぐため、フォームのロード時に各コントロールの「デザイン時の初期値(Left, Top, Width, Height)」を `Tag` プロパティに保存(または設計段階でハードコーディング、あるいはTagに `100,200,4000,300` のように持たせる)し、常に「初期値からの倍率」で計算し直す。これにより、何度ウィンドウを拡大・縮小させても、小数点の丸め誤差による破綻が起きなくなる。

3. エラーハンドリングにおけるデッドロック回避

APIで描画をロック(`0`)した状態でVBAのエラー(ゼロ除算やオブジェクト参照落ちなど)が発生すると、ウィンドウの描画が永遠にロックされたままフォームが操作不能(ゾンビ状態)になる。
必ず `On Error GoTo ErrorHandler` を仕込み、どのような例外パスを通ったとしても、`CleanUp` ラベルで確実に `WM_SETREDRAW` を `1` に戻す防衛的プログラミングが、シニアエンジニアの必須条件である。

4. 現場へ投入する際の手順と実務的アドバイス

1. Tagプロパティの仕込み
レスポンシブ化したいコントロールの `Tag` プロパティに、デザインビュー時点の値をカンマ区切りで入力する。

  • 例: `120,240,4500,300` (Left, Top, Width, Height の順)

2. アンカーの概念をコードで表現する

  • 横幅いっぱいに広げたいリストボックス: `scaleW` にウィンドウの拡大率を渡す。
  • 右下に固定したいボタン: `ratioX` の計算を工夫し、右端からの距離を一定に保つロジックに拡張する。

Accessは古い技術とやゆされることもあるが、そのアーキテクチャの根底にはWin32APIが直接流れている。プラットフォームの仕様を正確に理解し、メモリと描画のライフサイクルをコントロール下人にこそ、Accessは最強の業務ソリューションとして応えてくれる。

泥臭いレガシーシステムを、洗練されたモダンなインターフェースへ昇華させるために、この知見を現場の武器として役立ててほしい。

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