中小企業向けのアプリを作成しました。オフィスの一部の従業員はフォームを正しく見ることができません。その理由は、DPI設定が96dpi以上に設定されているためです。誰かがこれを制御する方法を知っていますか?
Winformsアプリの経験があるすべての人にとって、DPIがアプリケーションの外観に影響を与えないようにフォームレイアウトをどのように制御しますか?
ユーザーのUIフォントの選択(SystemFonts.IconTitleFont)を尊重せず、フォームを1つのフォントサイズ(Tahoma 8pt、Microsoft Sans Serif 8.25ptなど)のみにハードコーディングすると仮定すると、フォームのAutoScaleMode
から_ScaleMode.Dpi
_。
これにより、フォームのサイズがスケーリングされ、その子が制御するほとんどは、Form.Scale()
を呼び出すことにより、係数_CurrentDpiSetting / 96
_によって制御され、保護されたものが呼び出されます。 ScaleControl()
メソッドはそれ自体とすべての子コントロールで再帰的に実行されます。 ScaleControl
は、新しい倍率の必要に応じて、コントロールの位置、サイズ、フォントなどを増やします。
警告:すべてのコントロールが適切にスケーリングされるわけではありません。たとえば、リストビューの列は、フォントが大きくなっても広くなりません。これを処理するには、必要に応じて手動で追加のスケーリングを実行する必要があります。これを行うには、保護された
ScaleControl()
メソッドをオーバーライドし、リストビュー列を手動でスケーリングします。_public class MyForm : Form { protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { base.ScaleControl(factor, specified); Toolkit.ScaleListViewColumns(listView1, factor); } } public class Toolkit { /// <summary> /// Scale the columns of a listview by the Width scale factor specified in factor /// </summary> /// <param name="listview"></param> /// <param name="factor"></param> /// <example>/* /// protected override void ScaleControl(SizeF factor, BoundsSpecified specified) /// { /// base.ScaleControl(factor, specified); /// /// //ListView columns are not automatically scaled with the ListView, so we /// //must do it manually /// Toolkit.ScaleListViewColumns(lvPermissions, factor); /// } ///</example> public static void ScaleListViewColumns(ListView listview, SizeF factor) { foreach (ColumnHeader column in listview.Columns) { column.Width = (int)Math.Round(column.Width * factor.Width); } } }
_
コントロールを使用しているだけであれば、これはすべてうまくいきます。ただし、ハードコードされたピクセルサイズを使用する場合は、フォームの現在の倍率でピクセルの幅と長さをスケーリングする必要があります。ハードコードされたピクセルサイズを持つ可能性のある状況のいくつかの例:
この場合、ハードコードされた値を「現在のスケーリング係数」でスケーリングする必要があります。残念ながら、「現在の」スケール係数は提供されていないため、自分で記録する必要があります。解決策は、最初はスケーリング係数が1.0であり、ScaleControl()
が呼び出されるたびに、実行中のスケール係数を新しい係数で変更すると想定することです。
_public class MyForm : Form
{
private SizeF currentScaleFactor = new SizeF(1f, 1f);
protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
{
base.ScaleControl(factor, specified);
//Record the running scale factor used
this.currentScaleFactor = new SizeF(
this.currentScaleFactor.Width * factor.Width,
this.currentScaleFactor.Height * factor.Height);
Toolkit.ScaleListViewColumns(listView1, factor);
}
}
_
最初のスケーリング係数は_1.0
_です。次にフォームが_1.25
_でスケーリングされると、スケーリング係数は次のようになります。
_1.00 * 1.25 = 1.25 //scaling current factor by 125%
_
次にフォームが_0.95
_でスケーリングされると、新しいスケーリング係数は次のようになります。
_1.25 * 0.95 = 1.1875 //scaling current factor by 95%
_
(単一の浮動小数点値ではなく)SizeF
が使用される理由は、スケーリング量がx方向とy方向で異なる可能性があるためです。フォームが_ScaleMode.Font
_に設定されている場合、フォームは新しいフォントサイズに拡大縮小されます。フォントのアスペクト比は異なる場合があります(例:Segoe UIはTahomaよりも背の高いフォントです)。つまり、x値とy値を個別にスケーリングする必要があります。
したがって、位置_(11,56)
_にコントロールを配置する場合は、位置コードを次のように変更する必要があります。
_Point pt = new Point(11, 56);
control1.Location = pt;
_
に
_Point pt = new Point(
(int)Math.Round(11.0*this.scaleFactor.Width),
(int)Math.Round(56.0*this.scaleFactor.Height));
control1.Location = pt;
_
フォントサイズを選択する場合も同様です。
_Font f = new Font("Segoe UI", 8, GraphicsUnit.Point);
_
次のようになる必要があります:
_Font f = new Font("Segoe UI", 8.0*this.scaleFactor.Width, GraphicsUnit.Point);
_
また、32x32アイコンをビットマップに抽出すると、次のように変更されます。
_Image i = new Icon(someIcon, new Size(32, 32)).ToBitmap();
_
に
_Image i = new Icon(someIcon, new Size(
(int)Math.Round(32.0*this.scaleFactor.Width),
(int)Math.Round(32.0*this.scaleFactor.Height))).ToBitmap();
_
等.
非標準のDPIディスプレイのサポートは すべての開発者が支払うべき税金 です。しかし、誰も望んでいないという事実が理由です Microsoftは、グラフィックカードが高dpiを適切に処理するとは言わないアプリケーションを拡張する機能を放棄し、Vistaに追加しました 。
グローバル検索/置換を介してAutoScaleModeをすべての場所(つまり、すべてのUserControls)に継承するように設定してから、メインフォームでAutoScaleModeをDpiに設定します。
また、このタイプの状況では、レイアウトコンテナがアンカーよりもうまく機能することもわかりました。
やや抜本的だとは思いますが、アプリをWPFで書き直すことを検討してください。 WPFアプリケーションは、すべてのDPI設定で同じ外観になります。