Как управлять DPI шрифта в приложении.NET WinForms
Я создал приложение для малого бизнеса. Некоторые сотрудники в офисе не могут правильно видеть форму. Причина в том, что для них установлено значение DPI более 96dpi. Кто-нибудь знает способ контролировать это?
Как вы управляете макетом формы для всех, кто имеет опыт работы с приложениями winforms, чтобы DPI не влияло на внешний вид приложения?
3 ответа
Предполагая, что вы не пытаетесь выполнить выбор шрифта пользовательского интерфейса пользователя (SystemFonts.IconTitleFont) и жестко закодировать свои формы только для одного размера шрифта (например, 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); } } }
Это все хорошо, если вы просто используете элементы управления. Но если вы когда-либо будете использовать жестко запрограммированные размеры пикселей, вам нужно будет масштабировать ширину и длину вашего пикселя в соответствии с текущим масштабным коэффициентом формы. Некоторые примеры ситуаций, которые могут иметь жестко запрограммированные размеры пикселей:
- рисование прямоугольника высотой 25 пикселей
- нанесение изображения в месте (11,56) на бланке
- растянуть рисунок иконы до 48х48
- рисование текста с использованием 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 возможность для графической карты растягивать любые приложения, которые не говорят, что они должным образом обрабатывают высокое разрешение.
Установите AutoScaleMode на Inherit везде (т. Е. Все ваши UserControls) через глобальный поиск / замену, затем установите AutoScaleMode на Dpi в главной форме.
Я также обнаружил, что контейнеры компоновки работают лучше, чем якоря для подобных ситуаций.
Я знаю, что это несколько радикально, но подумайте о том, чтобы переписать ваше приложение в WPF. Приложения WPF выглядят одинаково на всех настройках DPI.