【実務・中級編】Windows FormsにおけるDPI変更動的検知:DynamicDPI対応でモニター移動時のレイアウト崩れを防ぐ – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

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`ファイルを開き、以下のセクションをアンコメント(有効化)せよ。




PerMonitorV2

—

ステップ2:DPI変更を検知し、レイアウトを再構築する

次に、コード側でイベントをハンドルする。`.NET Framework 4.7`以降であれば、`DpiChanged`イベントが提供されている。これを活用して、フォームの拡大率に合わせてコントロールのレイアウトを動的に補正する。

以下は、保守性を担保しつつ、最小限の記述で実装するプロダクションコードのテンプレートだ。

”’

”’ DPI変更検知・再レイアウトクラス
”’

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`のハンドリングを実装し、今日からあなたのツールを「プロの道具」に昇華させよ。

健闘を祈る。

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