C#でフォームアプリケーションを使用しています。モニターのDPIを変更すると、すべてのコントロールが移動します。コードthis.AutoScaleMode = AutoScaleMode.Dpi
を使用しましたが、問題を回避できませんでした。
誰にもアイデアがありますか?
編集:.NET 4.7の時点で、WindowsフォームはHigh DPIのサポートを改善しました。 docs.Microsoft.comで詳細をご覧ください ただし、Win 10 Creators Update以降でのみ機能するため、ユーザーベースによってはまだ使用できない可能性があります。
難しいが、不可能ではない。もちろん、WPFに移行することをお勧めしますが、それは現実的ではありません。
私はこの問題にかなりの時間を費やしました。 FlowLayoutPanelまたはTableLayoutPanelを使用せずに正しく動作させるためのルール/ガイドラインを次に示します。
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); // for design in 96 DPI
これらのガイドラインに従えば、特定のアンカーを持つコントロールを配置し、フローパネルを使用しなくても大丈夫です。このように構築されたアプリは、異なるDPIセットアップを備えた数百台のマシンにデプロイされており、苦情はもうありません。すべてのフォーム/コンテナ/グリッド/ボタン/テキストフィールドなどのサイズは、フォントと同様に正しくスケーリングされます。画像も機能しますが、高DPIでは少しピクセル化される傾向があります。
編集:このリンクには、特にAutoScaleMode.DPIの使用を選択した場合に、多くの有益な情報があります: 関連するstackoverflowの質問へのリンク
最後に、画面の向きと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);
}
私にとって魅力的な作品です。
注:これは、dpiが変更されたときに移動するコントロールを修正しません。これはぼやけたテキストのみを修正します!!。
高dpi設定でぼやけたWindowsフォームを修正する方法:
次に、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/
これはWindowsの問題のようです。これらの2行を削除することですべてが修正されました。
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
これは私が解決策を得た場所です:
Windows FormsでDPI対応アプリケーションを設計するのは非常に困難です。 DPIが変更されたときに適切にサイズ変更されるレイアウトコンテナー(TableLayoutPanelやFlowLayoutPanelなど)を使用する必要があります。すべてのコントロールもサイズ変更が必要です。これらのコンテナの構成は難しい場合があります。
単純なアプリケーションの場合は妥当な時間内に実行できますが、大規模なアプリケーションの場合は本当に多くの作業が必要です。
経験から:
AutoScaleMode
プロパティをNone
に設定します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])へのフォーム呼び出しにコントロールを動的に追加する場合。
コンストラクターの終了後にコントロールのサイズまたは場所を動的に設定する場合、次の関数のいずれかを作成および使用して、サイズまたは場所のスケーリングされた値を取得します:ScaleByDPI_X\ScaleByDPI_Y\ScaleByDPI_Size\ScaleByDPI_Point
アプリケーションをDPI対応としてマークするには、dpiAware要素をアプリケーションのアセンブリマニフェストに追加します。
すべてのControl.FontのGraphicsUnitをSystem.Drawing.GraphicsUnit.Pointに設定します
すべてのコンテナの* .Designer.csファイルで、AutoScaleMode値をSystem.Windows.Forms.AutoScaleMode.Noneに設定します
comboBoxやTextBoxなどのコントロールでは、Control.Size.Hieghtを変更しても影響はありません。この場合、Control.Font.Sizeを変更すると、コントロールの高さが修正されます。
フォームのStartPosition値がFormStartPosition.CenterScreenの場合、ウィンドウの場所を再計算する必要があります。