こんにちは。長年、Windowsという巨大なOSの深淵で、泥臭い自動化やアプリケーション開発と格闘してきたエンジニアです。
今日は、Windows Forms開発における「避けては通れない壁」、Dynamic DPI(動的DPI)対応についてお話しします。
「作ったアプリを別のモニターに移動させたら、文字がぼやけたり、ボタンが画面外に消えたりした」。そんな経験はありませんか? それはあなたのコードが悪いのではなく、Windowsの進化にUIが追いついていないだけ。今日はその「崩れ」を根本から解決する極意を伝授します。
—
なぜ、モニターを移動すると「崩れる」のか?
Windowsには「DPI(Dots Per Inch)」という概念があります。高精細なモニターでは、表示を拡大(125%、150%など)して文字を読みやすくしています。
問題は、「モニターごとにDPIが違う」という今の時代の当たり前です。
Windows Formsの古い設計は、「アプリ起動時にDPIが決まったら、一生そのまま」という前提で作られていました。モニターをまたぐ瞬間に「おい、解像度が変わったぞ!」という通知を受け取って自らレイアウトを再計算しないと、WindowsのOS側が無理やり拡大縮小(Bitmap Stretch)をかけてしまい、結果として「ぼやけ」が発生するのです。
—
1. まずは「高DPI対応」を宣言する
コードを書く前に、必ず設定が必要です。プロジェクト内の `app.manifest` を開き、以下の設定が有効になっているか確認してください。これがないと、どれだけコードを書いてもOSが勝手にボカシ処理をしてしまいます。
—
2. Dynamic DPIを制御する魔法のイベント
VB.NETでモニター移動を検知するには、フォームの `DpiChanged` イベントを使います。ここが今回の核心です。
Public Class MainForm
Public Sub New()
InitializeComponent()
‘ DPIが変更された瞬間に発生するイベントをフックします
AddHandler Me.DpiChanged, AddressOf OnDpiChanged
End Sub
Private Sub OnDpiChanged(sender As Object, e As DpiChangedEventArgs)
‘ ここでレイアウトを再計算します
‘ e.DeviceDpiNew に新しいDPIの値が入っています
Debug.WriteLine($”DPIが変更されました: {e.DeviceDpiNew}”)
‘ 必要に応じてコントロールの再配置やフォントの調整を行います
ApplyLayoutScaling(e.DeviceDpiNew)
End Sub
Private Sub ApplyLayoutScaling(newDpi As Integer)
‘ フォントの再計算や、特殊なレイアウト調整をここで行う
‘ 例: フォーム上のフォントを現在のDPIに合わせて再設定する
Me.Font = New Font(Me.Font.FontFamily, 9 (newDpi / 96.0F))
End Sub
End Class
なぜこれで解決するのか?
`PerMonitorV2` モードにすると、OSはアプリに対して「おい、今からこっちのモニターに行くぞ。新しいDPIはこれだ!」と `DpiChanged` イベントで教えてくれるようになります。私たちはその瞬間に、フォントサイズやコントロールのサイズを数学的に再計算してあてがうだけなのです。
—
3. 初学者が陥りやすい「レイアウトの罠」
コードを書く際、以下の3点に注意してください。ここをクリアすれば、あなたはもう初学者卒業です。
1. 絶対配置(Locationプロパティ)を避ける
`Button1.Left = 100` のように直書きすると、DPIが変わった瞬間に崩れます。`TableLayoutPanel` や `FlowLayoutPanel` を活用し、`Dock` や `Anchor` プロパティを使って「相対的な位置関係」を定義してください。
2. フォントのハードコーディング
`New Font(“MS UI Gothic”, 12)` のようにサイズを固定しないでください。`96 DPI`(標準)を基準として、倍率を掛ける癖をつけましょう。
3. 計算の浮動小数点エラー
`newDpi / 96.0F` のように、必ず `Float` 型(単精度浮動小数点)で計算してください。整数で割り算すると、わずかな誤差が積み重なり、ボタンが数ピクセルずつズレていきます。
—
最後に:先輩からのアドバイス
「完璧なレイアウト」を目指して泥沼にハマる必要はありません。まずは、「モニターをまたいだ時に、一度だけUIを再描画する」という仕組みを一つ作るだけで、あなたのアプリの信頼性は劇的に向上します。
VB.NETは古い言語だと言われることもありますが、Windowsのネイティブな挙動を制御する力は、現代のどのフレームワークよりも直感的で力強いものです。
迷ったら、まずは `DpiChanged` イベントの中に `MessageBox.Show(“変わったよ!”)` を仕込んでみてください。モニターをまたぐたびにメッセージが出る――その瞬間が、あなたが「動的なUI」を掌握した記念日になるはずです。
応援しています。何かあれば、いつでもコードを添えて相談してくださいね。
