Подтвердить что ты не робот

Как управлять шрифтом DPI в приложении .NET WinForms

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

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

4b9b3361

Ответ 1

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

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

Предупреждение: Не все элементы управления должным образом масштабируются. Столбцы listview, например, не получит шире по мере увеличения шрифта. В чтобы справиться с тем, что вам придется вручную выполните дополнительное масштабирование как обязательный. я делаю это, переопределяя защищенный метод ScaleControl() и масштабирование столбцов listview вручную:

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 возможность для графической карты растянуть любые приложения, которые не говорят, что они правильно обрабатывают высокие -dpi.

Ответ 2

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

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

Ответ 3

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