C# でフォーム アプリケーションを作成しています。モニターの DPI を変更すると、すべてのコントロールが移動します。コードを使用しましたthis.AutoScaleMode = AutoScaleMode.Dpi
が、問題は回避されませんでした。
誰にもアイデアはありますか?
編集: .NET 4.7 の時点で、Windows フォームは高 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 を使用することを選択した場合に、多くの良い情報があります:関連するスタックオーバーフローの質問へのリンク
画面の向きと 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);
}
私にとって魅力のように機能します。
これはWindowsの問題のようです。これらの2行を取り出すと、すべてが修正されました。
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
これが私が解決策を得た場所です:
Windows フォームで DPI 対応アプリケーションを設計するのは非常に困難です。DPI が変更されたときに適切にサイズ変更されるレイアウト コンテナー (TableLayoutPanel や FlowLayoutPanel など) を使用する必要があります。すべてのコントロールのサイズも変更する必要があります。これらのコンテナーの構成は難しい場合があります。
単純なアプリケーションの場合は妥当な時間内に実行できますが、大規模なアプリケーションの場合は非常に多くの作業が必要になります。
経験から:
AutoScaleMode
プロパティをに設定しますNone
Winform アプリケーション フォームはコンテンツ コントロールと画像を含む可能性があるため、システムがウィンドウのサイズを変更できるようにすることは解決策ではありませんが、適切にスケーリングされた画像を使用して、DPI 解像度ごとに 1 つのフォームを持つことができれば...それは良い考えではありません。画面サイズが大きくなるにつれて、フォントサイズが小さくなるためです。
異なる DPI 解像度を使用する場合、システムはフォームにそのコントロールのサイズ、位置、およびフォントを強制的に再定義させますが、画像ではありません。解決策は、ロード時に実行時にフォームの DPI を変更して、すべてが元のサイズと位置に戻るようにすることです。
これは可能な解決策であり、80個の画像ボタン、TabControlsなどを持っているカードゲームアプリケーションでテストしました.
各フォームの form_Load イベントで、次のコード スニペットを追加します。
Dim dpi As Graphics = Me.CreateGraphics Select Case dpi.DpiX Case 120 '-- Do nothing if your app has been desigbned with 120 dpi
Case Else
'-- I use 125 AND NOT 120 because 120 is 25% more than 96
Me.Font = New Font(Me.Font.FontFamily, Me.Font.Size * 125 / dpi.DpiX)
End Select
さらに、再起動せずに同じコンピューターでさまざまな解像度をテストするための簡単なトリック:
コントロール パネルから、解像度を変更します。再起動しないでください!代わりに、セッションを閉じて、同じユーザーで新しいセッションを開きます。
別の注意点があります。実行時にコントロールのサイズと位置を設定する場合は、同じ DPI 係数 (例: 125 / Dpi.Dpix) を新しい座標に適用する必要があります。したがって、application.startup イベントから DPIFactor グローバル変数を設定することをお勧めします。
最後だが大事なことは:
元の解像度とは別の解像度から Visual Studio でアプリケーションを開かないでください。そうしないと、各フォームを開くたびにすべてのコントロールが移動してサイズが変更され、戻る方法がありません...
これが役に立てば幸いです、幸せなプログラミング。