38

中小企業向けのアプリを作成しました。オフィスの一部の従業員は、フォームを正しく見ることができません。その理由は、DPI 設定が 96dpi 以上に設定されているためです。これを制御する方法を知っている人はいますか?

Winforms アプリを使用した経験のあるすべての人にとって、フォーム レイアウトを制御して、DPI がアプリケーションの外観に影響を与えないようにするにはどうすればよいでしょうか?

4

3 に答える 3

63

ユーザーの UI フォントの選択 (SystemFonts.IconTitleFont) を尊重しようとせず、フォームを 1 つのフォント サイズのみ (たとえば、Tahoma 8pt、Microsoft Sans Serif 8.25pt) にハードコードすると仮定すると、フォームを に設定できAutoScaleModeますScaleMode.Dpi

これにより、フォームとそのほとんどの子コントロールのサイズが をCurrentDpiSetting / 96呼び出すことによって倍率で拡大縮小され、それ自体とすべての子コントロールでForm.Scale()保護された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);
      }
   }
}

コントロールを使用しているだけであれば、これで問題ありません。ただし、ハードコードされたピクセル サイズを使用する場合は、フォームの現在の倍率でピクセルの幅と長さを調整する必要があります。ピクセル サイズがハードコーディングされている可能性がある状況の例:

  • 高さ25pxの長方形を描く
  • フォームの位置 (11,56) に画像を描画する
  • アイコンの描画を 48x48 に引き伸ばす
  • Microsoft Sans Serif 8.25pt を使用したテキストの描画
  • アイコンの 32x32 形式を取得し、PictureBox に詰め込む

この場合、ハードコードされた値を「現在のスケーリング係数」でスケーリングする必要があります。残念ながら、「現在の」倍率は提供されていません。自分で記録する必要があります。解決策は、最初は倍率が 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(単一の浮動小数点値ではなく) a が使用される理由は、スケーリング量が x 方向と y 方向で異なる可能性があるためです。フォームが に設定されている場合、フォームはScaleMode.Font新しいフォント サイズにスケーリングされます。フォントは異なる縦横比を持つことができます (たとえば、 Segoe UIはTahomaよりも背の高いフォントです)。これは、x 値と y 値を個別にスケーリングする必要があることを意味します。

したがって、コントロールを location(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 に追加した理由です。

于 2008-10-14T17:59:48.707 に答える
16

AutoScaleMode をグローバル検索/置換を介してどこでも (つまり、すべての UserControls) 継承するように設定し、メイン フォームで AutoScaleMode を Dpi に設定します。

また、このような状況では、レイアウト コンテナがアンカーよりもうまく機能することもわかりました。

于 2008-10-09T14:56:21.417 に答える
2

やや抜本的であることは承知していますが、アプリを WPF で書き直すことを検討してください。WPF アプリケーションは、すべての DPI 設定で同じ外観になります。

于 2014-11-06T16:39:02.977 に答える