Как управлять DPI шрифта в приложении .NET WinForms - PullRequest
32 голосов
/ 09 октября 2008

Я создал приложение для малого бизнеса. Некоторые сотрудники в офисе не могут правильно видеть форму. Причина в том, что для них установлено значение DPI более 96 dpi. Кто-нибудь знает способ контролировать это?

Для всех вас, кто имеет опыт работы с приложениями winforms, как вы управляете макетом формы, чтобы DPI не влиял на внешний вид приложения?

Ответы [ 3 ]

63 голосов
/ 14 октября 2008

Предполагая, что вы не пытаетесь выполнить выбор шрифта пользовательского интерфейса пользователя (SystemFonts.IconTitleFont) и жестко закодировать свои формы только для одного размера шрифта (например, Tahoma 8pt, Microsoft Sans Serif 8.25pt), вы можете установить * форму От 1001 * до ScaleMode.Dpi.

Это масштабирует размер формы и большинство ее дочерних элементов управления с коэффициентом CurrentDpiSetting / 96, вызывая Form.Scale(), который в свою очередь вызывает защищенный метод ScaleControl() рекурсивно для себя и для всех дочерние элементы управления. ScaleControl увеличит позицию, размер, шрифт и т. Д. Элемента управления, необходимые для нового коэффициента масштабирования.

Предупреждение: Не все элементы управления правильно масштабируются. Столбцы listview, например, не получит шире, поскольку шрифт становится больше. В Для того, чтобы справиться с этим вам придется вручную выполнить дополнительное масштабирование как требуется. я делаю это, переопределяя защищенный 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 (а не одного значения с плавающей запятой) заключается в том, что величины масштабирования могут различаться в направлениях 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 отказалась и добавила в Vista возможность для графической карты растягивать любые приложения, которые не говорят, что они должным образом обрабатывают высокое разрешение .

16 голосов
/ 09 октября 2008

Установите AutoScaleMode на Наследовать везде (т.е. все ваши UserControls) с помощью глобального поиска / замены, затем установите AutoScaleMode на Dpi в главной форме.

Я также считаю, что контейнеры компоновки работают лучше, чем якоря для таких ситуаций.

2 голосов
/ 06 ноября 2014

Я знаю, что это несколько радикально, но подумайте о том, чтобы переписать ваше приложение в WPF. Приложения WPF выглядят одинаково при каждом значении DPI.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...