Windows Formsの呪縛を解く:マルチモニター環境でUIを崩壊させない「Dynamic DPI」の極意
現場のエンジニア諸君。今やマルチモニター環境は標準だ。しかし、デスクトップPCの4Kモニターから、会議室の低解像度プロジェクターへウィンドウをドラッグした瞬間、UIが「ゴミ」のように崩れた経験はないか?
「Visual Basic.NETのWindows Formsはレガシーだから仕方ない」と諦めるのは、技術者として怠慢だ。Windows Formsは、適切にハンドリングすれば、現代の可変DPI環境でも完璧に振る舞う。
今回は、モニター移動時にUIが破綻する根本原因を突き止め、それをプログラム側で「検知」し、再計算させるための「動的DPI(Per-Monitor DPI)」対応の極意を伝授する。
—
なぜ、あなたのUIはモニター移動で崩れるのか
Windows Formsのデフォルト設定では、アプリケーションのDPI認識は「システム単位(System Aware)」になっていることが多い。これは、アプリが起動した時のモニター設定を基準に固定されるため、別のDPIを持つモニターへ移動した瞬間にOSが「強制拡大(ビットマップ引き伸ばし)」を行い、ボケたフォントと重なり合ったコントロールという悲惨なUIを生み出す。
これを解決する唯一の正解は、`PerMonitorV2` モードを有効にし、OSからの通知をフックしてレイアウトを「再計算」させることだ。
—
ステップ1:アプリのマニフェストを書き換える
まず、プロジェクトのプロパティだけでは不十分だ。アプリが「私はDPIの変化を理解できる」とWindowsに宣言しなければならない。`app.manifest`ファイルを開き、以下のセクションをアンコメント(有効化)せよ。
—
ステップ2:DPI変更を検知し、レイアウトを再構築する
次に、コード側でイベントをハンドルする。`.NET Framework 4.7`以降であれば、`DpiChanged`イベントが提供されている。これを活用して、フォームの拡大率に合わせてコントロールのレイアウトを動的に補正する。
以下は、保守性を担保しつつ、最小限の記述で実装するプロダクションコードのテンプレートだ。
”’
”’
Public Class DpiAwareForm
Inherits Form
Public Sub New()
‘ DPI変更イベントを購読
AddHandler Me.DpiChanged, AddressOf OnDpiChanged
End Sub
”’
”’
Private Sub OnDpiChanged(sender As Object, e As DpiChangedEventArgs)
‘ ログ出力(デバッグ用)
Debug.WriteLine($”DPIが {e.OldDpi} から {e.NewDpi} に変更されました”)
‘ ここで独自のレイアウト調整ロジックを走らせる
‘ スケール比率を計算し、コントロールのフォントや位置を再計算
Dim scaleFactor As Single = e.NewDpi / e.OldDpi
ApplyScaling(Me, scaleFactor)
End Sub
”’
”’
Private Sub ApplyScaling(parent As Control, factor As Single)
Me.SuspendLayout()
For Each ctrl As Control In parent.Controls
‘ フォントサイズの調整
ctrl.Font = New Font(ctrl.Font.FontFamily, ctrl.Font.Size factor, ctrl.Font.Style)
‘ 位置とサイズの調整
ctrl.Location = New Point(CInt(ctrl.Location.X factor), CInt(ctrl.Location.Y factor))
ctrl.Size = New Size(CInt(ctrl.Size.Width factor), CInt(ctrl.Size.Height factor))
‘ 子要素があれば再帰的に処理
If ctrl.HasChildren Then ApplyScaling(ctrl, factor)
Next
Me.ResumeLayout()
End Sub
End Class
—
実務で絶対に守るべき「3つの鉄則」
コードをコピペするだけでは一流にはなれない。業務ツールとして永続的に運用するために、以下の設計思想を頭に叩き込め。
1. `SuspendLayout` / `ResumeLayout` は必須
再計算処理を行う際、これらを使わなければ、コントロールが一つ移動するたびに再描画が発生し、画面が激しくチラつく。パフォーマンスを殺すな。
2. 絶対座標指定は悪(あく)
`Top`や`Left`をハードコードしているUIは、DPI変更で必ず死ぬ。`TableLayoutPanel`や`FlowLayoutPanel`を駆使し、レイアウトを「自動伸縮」に依存させろ。これが現代のUI設計の基本だ。
3. データベース/ファイル連携との分離
UIの再計算処理と、データロード処理は絶対に混ぜるな。DPI変更時にデータが再読み込みされるような設計にすると、ユーザーがモニターを移動するたびに通信が発生する「ゴミアプリ」が完成する。状態保持はビジネスロジック層で行え。
—
結論:技術は「逃げ」を許さない
マルチモニター環境への対応は、単なる「見た目の調整」ではない。それは、あなたが開発するツールが、ユーザーの環境の変化にどれだけ柔軟に追従できるかという「信頼性」そのものだ。
「VB.NETだから古い」のではない。「古い手法で書いているからVB.NETが古く見える」だけだ。今回紹介した`PerMonitorV2`と`DpiChanged`のハンドリングを実装し、今日からあなたのツールを「プロの道具」に昇華させよ。
健闘を祈る。
