2018年11月19日月曜日

高DPI環境で作るフォームアプリケーション(C#)

フォームアプリケーションの高DPI対応

高DPI対応のアプリケーションを作りたいならWPF使えって話ですが、なにかとフォームアプリケーションの方が重宝したり。高DPI対応の方法はググればいくらでも出てきますが、それでもレイアウトが崩れたりすることがありました。今回はそれに対する対処法をなんとか見つけました。

高DPI対応方法

Program.csを編集する方法とマニフェストファイルを追加する方法がありますが、私はProgram.csの方が好きなので一応そちらを紹介。

Program.csを開いてclass Programの中に以下をコピペ。
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();
static void Main()の冒頭にでもSetProcessDPIAware()を書いておく。

まとめるとこう。
static class Program
{
    [STAThread]
    static void Main()
    {
        SetProcessDPIAware();
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool SetProcessDPIAware();
} 

以上により高DPI対応は完了します。
確かにこれで高DPI環境下でも綺麗にボケることなくフォームが生成されます。


しかし、高DPI環境で開発したものを通常DPI環境で起動するとレイアウトが破綻してしまいます。
この破綻は、フォームのフォントサイズを大きくしたり、フォントを変更したりすると発生しやすいです。今回はメイリオUIの12ptにしてみました。


これはFormクラスのAutoScaleModeプロパティがデフォルトの"Font"になっているためです。そこで、これを"Dpi"に「VisualStudioのプロパティウィンドウから」変更します。コード側から変更することは可能ですが、コントロールのレイアウトが行われる前にAutoScaleModeが変更されていることが必要です。
(例えば、Formクラスを継承したForm2クラスのインスタンスをShow()してフォーム表示するとした場合、コンストラクタを呼んだ時点でInitが呼ばれてレイアウトされてしまいます。つまりインスタンスを作ってからプロパティでAutoScaleModeを変更しても手遅れです。そのため、特別な事情がない限り、UIベースでAutoScaleModeプロパティを変更してしまったほうが簡単です。)
AutoScaleMode=Dpi にした結果がこちら。
いい感じですが、今度はRichTextBoxが狂ってしまいました。

これへの対処には苦労しましたが、 RichTextBoxのFontプロパティを明示的に指定することで回避できました。この場合も先程と同じくVisualStudioのUIから指定するのがお手軽です。インスタンス生成後にプロパティだけ変更しても効果はありませんでした。
(ちなみにUI上ではデフォルトのフォントには親フォームのフォントが指定されているように見えますが、Designer.csを見ればお分かりの通り、コード上では指定されていません。その場合は、UIからフォント指定ウィンドウを出し、一度別のフォントなりptなりを指定してOKしてから、再度フォント指定ウィンドウから親フォームと同じフォント・ptを指定すると、明示的に指定できます。プロパティの文字も太字になっていればOK。)
これで綺麗になりました。実際のところまだちょっとズレてますが、この程度は許容することにします。

まとめ

  1. Program.csでDPIAwareする
  2. FormクラスのAutoScaleModeをUIを使って"Dpi"に変更する
  3. それでもズレるコントロールは、FontをUIを使って明示的に指定してみる

0 件のコメント:

コメントを投稿