web-dev-qa-db-ja.com

DPI対応アプリケーションの作成

C#でフォームアプリケーションを使用しています。モニターのDPIを変更すると、すべてのコントロールが移動します。コードthis.AutoScaleMode = AutoScaleMode.Dpiを使用しましたが、問題を回避できませんでした。

誰にもアイデアがありますか?

63
RRR

編集:.NET 4.7の時点で、WindowsフォームはHigh DPIのサポートを改善しました。 docs.Microsoft.comで詳細をご覧ください ただし、Win 10 Creators Update以降でのみ機能するため、ユーザーベースによってはまだ使用できない可能性があります。


難しいが、不可能ではない。もちろん、WPFに移行することをお勧めしますが、それは現実的ではありません。

私はこの問題にかなりの時間を費やしました。 FlowLayoutPanelまたはTableLayoutPanelを使用せずに正しく動作させるためのルール/ガイドラインを次に示します。

  • 常にデフォルトの96 DPI(100%)でアプリを編集/設計します。 120DPI(125%f.ex)で設計する場合、後でそれを使用するために96 DPIに戻ると非常に悪くなります。
  • AutoScaleMode.Fontを使用して成功しましたが、AutoScaleMode.DPIをあまり試していません。
  • すべてのコンテナ(フォーム、パネル、タブページ、ユーザーコントロールなど)で必ずデフォルトのフォントサイズを使用してください。 8,25ピクセル。コンテナクラスのデフォルトフォントを使用するように、すべてのコンテナの.Designer.csファイルで設定しないでください。
  • すべてのコンテナmustは同じAutoScaleModeを使用します
  • すべてのコンテナにDesigner.csファイルで以下の行が設定されていることを確認してください。

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); // for design in 96 DPI

  • ラベル/テキストボックスなどに異なるフォントサイズを設定する必要がある場合、winformsはコンテナのフォント設定を使用してコンテンツをスケーリングし、異なるフォントサイズのパネルをf.exにするため、コンテナクラスにフォントを設定するのではなく、コントロールごとに設定します含まれているフォームよりも問題が発生することが保証されています。フォームとフォーム上のすべてのコンテナが同じフォントサイズを使用している場合は動作する可能性がありますが、私は試していません。
  • より高いDPI設定で別のマシンまたは仮想Windowsインストール(VMware、Virtual PC、VirtualBox)を使用して、設計をすぐにテストします。 DEVマシンの/ bin/Debugフォルダーからコンパイル済みの.exeファイルを実行するだけです。

これらのガイドラインに従えば、特定のアンカーを持つコントロールを配置し、フローパネルを使用しなくても大丈夫です。このように構築されたアプリは、異なるDPIセットアップを備えた数百台のマシンにデプロイされており、苦情はもうありません。すべてのフォーム/コンテナ/グリッド/ボタン/テキストフィールドなどのサイズは、フォントと同様に正しくスケーリングされます。画像も機能しますが、高DPIでは少しピクセル化される傾向があります。

編集:このリンクには、特にAutoScaleMode.DPIの使用を選択した場合に、多くの有益な情報があります: 関連するstackoverflowの質問へのリンク

102
Trygve

最後に、画面の向きとDPIの両方の処理の問題の解決策を見つけました。
Microsoftはすでにそれを説明するドキュメントを提供していますが、DPI処理を完全に停止させる小さな欠陥があります。 「各方向の個別のレイアウトコードの作成」の下のドキュメントで提供されているソリューションに従ってください http://msdn.Microsoft.com/en-us/library/ms838174.aspx

次に重要な部分!それぞれの最後のLandscape()およびPortrait()メソッドのコード内に次の行を追加します。

this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;

したがって、これら2つのメソッドのコードは次のようになります。

protected void Portrait()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(88, 216);
   this.crawlTime.Size = new System.Drawing.Size(136, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(10, 216);
   this.crawlTimeLabel.Size = new System.Drawing.Size(64, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(88, 200);
   this.crawlStartTime.Size = new System.Drawing.Size(136, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(10, 200);
   this.crawlStartedLabel.Size = new System.Drawing.Size(64, 16);
   this.light1.Location = new System.Drawing.Point(208, 66);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(192, 66);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(88, 182);
   this.linkCount.Size = new System.Drawing.Size(136, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(10, 182);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 84);
   this.currentPageBox.Size = new System.Drawing.Size(214, 90);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 68);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(214, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(10, 48);
   this.noProxyCheck.Size = new System.Drawing.Size(214, 20);
   this.startButton.Location = new System.Drawing.Point(8, 240);
   this.startButton.Size = new System.Drawing.Size(216, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 24);
   this.addressBox.Size = new System.Drawing.Size(214, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

protected void Landscape()
{
   this.SuspendLayout();
   this.crawlTime.Location = new System.Drawing.Point(216, 136);
   this.crawlTime.Size = new System.Drawing.Size(96, 16);
   this.crawlTimeLabel.Location = new System.Drawing.Point(160, 136);
   this.crawlTimeLabel.Size = new System.Drawing.Size(48, 16);
   this.crawlStartTime.Location = new System.Drawing.Point(64, 120);
   this.crawlStartTime.Size = new System.Drawing.Size(248, 16);
   this.crawlStartedLabel.Location = new System.Drawing.Point(8, 120);
   this.crawlStartedLabel.Size = new System.Drawing.Size(48, 16);
   this.light1.Location = new System.Drawing.Point(296, 48);
   this.light1.Size = new System.Drawing.Size(16, 16);
   this.light0.Location = new System.Drawing.Point(280, 48);
   this.light0.Size = new System.Drawing.Size(16, 16);
   this.linkCount.Location = new System.Drawing.Point(80, 136);
   this.linkCount.Size = new System.Drawing.Size(72, 16);
   this.linkCountLabel.Location = new System.Drawing.Point(8, 136);
   this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
   this.currentPageBox.Location = new System.Drawing.Point(10, 64);
   this.currentPageBox.Size = new System.Drawing.Size(302, 48);
   this.currentPageLabel.Location = new System.Drawing.Point(10, 48);
   this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
   this.addressLabel.Location = new System.Drawing.Point(10, 4);
   this.addressLabel.Size = new System.Drawing.Size(50, 16);
   this.noProxyCheck.Location = new System.Drawing.Point(168, 16);
   this.noProxyCheck.Size = new System.Drawing.Size(152, 24);
   this.startButton.Location = new System.Drawing.Point(8, 160);
   this.startButton.Size = new System.Drawing.Size(304, 20);
   this.addressBox.Location = new System.Drawing.Point(10, 20);
   this.addressBox.Size = new System.Drawing.Size(150, 22);

   //note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
   this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
   this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;   //IMPORTANT
   this.ResumeLayout(false);
}

私にとって魅力的な作品です。

14
user1251307

注:これは、dpiが変更されたときに移動するコントロールを修正しません。これはぼやけたテキストのみを修正します!!。


高dpi設定でぼやけたWindowsフォームを修正する方法:

  1. フォームデザイナーに移動し、フォームを選択します(タイトルバーをクリックします)
  2. F4を押して[プロパティ]ウィンドウを開き、
  3. 次に、AutoScaleModeプロパティを見つけます
  4. Font(デフォルト)からDpiに変更します。

次に、Program.cs(またはMainメソッドが配置されているファイル)に移動し、次のように変更します。

namespace myApplication
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            // ***this line is added***
            if (Environment.OSVersion.Version.Major >= 6)
                SetProcessDPIAware();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }

        // ***also dllimport of that function***
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool SetProcessDPIAware();
    }
}

保存してコンパイルします。これで、フォームが再びカリカリに見えるはずです。


ソース: http://crsouza.com/2015/04/13/how-to-fix-blurry-windows-forms-windows-in-high-dpi-settings/

9
bh_earth0

これはWindowsの問題のようです。これらの2行を削除することですべてが修正されました。

this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

これは私が解決策を得た場所です:

4
Jakobitz

Windows FormsでDPI対応アプリケーションを設計するのは非常に困難です。 DPIが変更されたときに適切にサイズ変更されるレイアウトコンテナー(TableLayoutPanelやFlowLayoutPanelなど)を使用する必要があります。すべてのコントロールもサイズ変更が必要です。これらのコンテナの構成は難しい場合があります。

単純なアプリケーションの場合は妥当な時間内に実行できますが、大規模なアプリケーションの場合は本当に多くの作業が必要です。

3
testalino

経験から:

  • 重要でない限り、WindowsフォームでDPI認識を使用しないでください。
  • このため、アプリのすべてのフォームとユーザーコントロールで、常にAutoScaleModeプロパティをNoneに設定します
  • 結果:DPI設定が変更されたときのインターフェイスのWYSIWYGタイプ
2
Liviu M.
  1. WinFormsアプリケーションをDPI対応アプリケーションにしたい場合、Trygveの良い答えに加えて、大きなプロジェクトがある場合は、フォームとそのコンテンツを自動的にスケーリングすることができます。ScaleByDPI関数を作成することでこれを行うことができます。

ScaleByDPI関数は、通常はフォームであるControlパラメーターを受け取り、すべてのサブコントロールを再帰的に繰り返し(if(control.HasChildren == true))、アプリケーションコントロールの位置とサイズを縮小し、フォントのサイズとサイズをOSが構成したDPI。画像、アイコン、グラフィックスにも実装してみてください。

ScaleByDPI関数に関する特記事項:

a。デフォルトのフォントサイズを持つすべてのコントロールでは、Font.Sizeを8.25に設定する必要があります。

b。 devicePixelRatioXおよびdevicePixelRatioYの値は、(control.CreateGraphics()。DpiX/96)および(control.CreateGraphics()。DpiY/96)によって取得できます。

c。 control.Dockとcontrol.Anchorの値に基づくアルゴリズムにより、Control.SizeとControl.Locationをスケールする必要があります。 control.Dockには6つの値のうち1つがあり、control.Anchorには16の値のうち1つがあることに注意してください。

d。このアルゴリズムでは、次のブール変数isDoSizeWidth、isDoSizeHeight、isDoLocationX、isDoLocationY、isDoRefactorSizeWidth、isDoRefactorSizeHeight、isDoRefactorLocationX、isDoRefactorLocationY、isDoClacLocationXBasedOnRight、isDoClacLocationYBasedOnBottomに値を設定する必要があります。

e。プロジェクトでMicrosoftコントロール以外のコントロールライブラリを使用している場合、このコントロールには特別な処理が必要になる場合があります。

上記の詳細(d。)bool変数:

*コントロールのグループ(ボタンの場合もある)を同じ垂直線上に次々と配置する必要があり、それらのアンカー値には右ではなく左が含まれるか、または同じ水平線上に次々に配置する必要があり、アンカー値にはボトムが含まれますが、トップは含まれません。この場合、コントロールのロケーション値を再計算する必要があります。

*アンカーに上下と左右が含まれるコントロールの場合、コントロールのサイズと位置の値をリファクタリングする必要があります。

ScaleByDPI関数の使用:

a。次のコマンドをフォームコンストラクターの最後に追加します:ScaleByDPI(this);

b。また、ScaleByDPI([ControlName])へのフォーム呼び出しにコントロールを動的に追加する場合。

  1. コンストラクターの終了後にコントロールのサイズまたは場所を動的に設定する場合、次の関数のいずれかを作成および使用して、サイズまたは場所のスケーリングされた値を取得します:ScaleByDPI_X\ScaleByDPI_Y\ScaleByDPI_Size\ScaleByDPI_Point

  2. アプリケーションをDPI対応としてマークするには、dpiAware要素をアプリケーションのアセンブリマニフェストに追加します。

  3. すべてのControl.FontのGraphicsUnitをSystem.Drawing.GraphicsUnit.Pointに設定します

  4. すべてのコンテナの* .Designer.csファイルで、AutoScaleMode値をSystem.Windows.Forms.AutoScaleMode.Noneに設定します

  5. comboBoxやTextBoxなどのコントロールでは、Control.Size.Hieghtを変更しても影響はありません。この場合、Control.Font.Sizeを変更すると、コントロールの高さが修正されます。

  6. フォームのStartPosition値がFormStartPosition.CenterScreenの場合、ウィンドウの場所を再計算する必要があります。

1
Yossi B