[C#] 高DPI環境下でWinFormsの画面レイアウトが崩れて大変だったけど何とかなった

  • 投稿日:
  • by
  • Category:

以前の記事の続き。実際に顧客の端末に導入しに行った時に起きた悲劇について。

端的に言えば「高DPI時に画面レイアウトが崩れて撃沈」した。

こちらで同様の事例が紹介されている。

demo.png

画像は上記リンク先より引用。文字がはみ出してレイアウトが崩れている。

WindowsのDPI設定を変更することでフォントサイズが大きくなり、コントロールのサイズからはみ出してしまったことが原因だろう。

画面レイアウト作成時には高DPI環境も想定なければならなかったのだが完全に失念していた。

 

 

高DPI環境とスケーリングのおはなし

こちらのスライドに詳しく解説がある(一から読むことを推奨するがここでは割愛)

ここで知っておきたいのは、スケーリングの方法には「Windows XP形式」と「DPI仮想化」の2種類があるということ。

  1. Windows XP形式」は高DPI環境下でも綺麗に表示されるが、アプリ側で対応していないとレイアウトが崩れる。
  2. DPI仮想化」はただ画像を拡大しているだけなのでレイアウトは崩れない代わりに表示がぼやける。

という特徴がある。

Windows7の場合、DPIの設定はコントロールパネルのディスプレイ設定から変更できる。

003.png

ここをクリックして、

002.png

「Windows XP 形式の DPI スケーリングを使用する」のチェックの有無で設定が変わる。デフォルトはオンだ

 

WindowsXP形式だと崩れる

上のチェックがオンだとどうなるかというと、初めの画像のようにレイアウトが崩れる。

逆にチェックを外せば「DPI仮想化」モードになり、ぼやけることを引き換えにレイアウト崩れを回避できる。

参考画像↓

007.png

百聞は一見に如かず、ということで参考サイトから引用。「DPI仮想化」の場合は単純に画像を引き延ばしているためぼやける。

アプリ側で対応する手間が不要なのでこれはこれでいいのだが、やるならとことんやりたい。ということでもうちょっと「Windows XP形式」の方を掘り下げてみる。

 

画面のスケーリングを無効化

色々とググっていると「画面のスケーリングを無効化」する設定があるという。

EXEファイルを右クリック→「プロパティ」→「互換性」タブのこれだ。

004.png

確かにある。でもチェックを入れても崩れたままだ。

どうやらこれは強制的に「Windows XP形式」で表示する、というだけの設定らしい。それはもうディスプレイ設定で指定済みだ。ハズレ。

 

マニフェストや構成ファイルの設定を変える

app.manifestのdpiAwareをtrueにする」とか「app.configのDpiAwarenessをPerMonitorV2にする」ことで、このアプリケーションはDPI awareだと宣言ができるらしい。のだが、結局行きつく先は上の「高 DPI 設定では画面のスケーリングを無効にする」にチェックを入れるのと同じことらしいのでスルー。

 

FormのAutoScaleModeを指定する

次に見つけたのがFormのAutoScaleModeプロパティ。

画面開発時にFormプロパティのAutoScaleModeDpiにすると、DPIに応じた画面レイアウトになるらしい。

005.png

確かに全く考慮しておらずNoneのままだった。これをDpiに変えると・・・レイアウトが戻った!!!WinForms終わってなかった!!!!

 

いくつか不具合がある

ひとまずレイアウトはうまく表示されたようだったが、よくよくテストしてみると一部のTextboxの高さが大きくなり過ぎていたり、DatagridViewのカラム幅がスケールされず横幅が足りない、といった問題があった。

こちらの掲示板でも

  • MultiLineのTextBox
  • RichTextBox
  • ListBox
  • CheckedListBox

の高さが高くなりすぎる問題が取り上げられている。

また、こちらのサイトでは

  • TextBoxをMultilineにしたときの高さ
  • ListViewをDetailsにしたときのColumnHeaderの幅

が正しくスケーリングされない、という指摘がある。

私の環境で起きたDatagridViewのカラム幅がスケールされない問題はこちらでも指摘されている。

 

完全対応させるには一工夫が必要

TextBoxの高さ問題については、Panelの中に入れてDock=Fillにすることで解決するようだ。

Panelは問題なくスケールされるため、その中にDock=FillにしたTextBoxを入れることでTextBoxのサイズがPanelに委ねられる、ということらしい。

少々厄介だったのがDataGridView。

コントロール自体やフォントサイズはスケーリングされるものの、幅が変わらないため表示できる文字数がかなり少なくなってしまう。

そこでプロパティを確認してみると、カラム幅を設定できる項目を発見。

006.png

AutoSizeColumnsModeColumnHeaderに。これでカラム幅はヘッダ文字列によって決まるようになり、ヘッダ文字がスケールされたらされた分だけ横幅が大きくなってくれるようになった。強引なやり方なので高DPI環境では若干右側に余りが出来るが、実用に問題がないということで許してもらった。

高さも足りていなかったのでAutoSizeRowsModeAllCellsに。これでフォントサイズに合わせて高さも調整されるようになった。

最後に、ヘッダの高さも調整するためにColumnHeadersHeightSizeModeAutoSizeを設定。これでなんちゃって高DPI対応ができた。

 

WinFormsはオワコンなのか

一方でWPFやUWPは標準で高DPIに対応しており、作りも今どきで新しい環境に適した作りになっている。

かたやWinFormsはVBやMFCなどの「過去の遺産」を置き換える立ち位置でもある。MVCモデルにも対応していないし、全体的に古臭いのは否めない。

とはいえWinFormsが使い物にならないかといえばそうでもない。UIをさほど重要視しない業務用アプリケーションの場合、WinFormsを選んでも大きなデメリットは無いだろう。寧ろ既存の情報が多く出揃っている分だけ開発の工数が見積もりやすい側面もある。

MVCモデルはあくまでデザインパターンの一つなわけで、開発時に適切なデザイン設計がなされているのであればMVCモデルにこだわる必要は無い。

また、長期に渡って運用・保守を行う前提のシステムの場合は枯れた仕組みの方が歓迎される。

そこらへんの良し悪しは案件ごとに大きく変わるので、一方から見てオワコンと決めつけるのは早計だろう。

もちろんその逆もまた然りなので、常にWPF/UWPも考慮しなければいけない。んだけどUWPはやっぱり好かんなぁ...。

コメントする