Независимость от разрешения

33

Традиционные Windows-приложения связаны определенными предположениями относительно разрешения экрана. Обычно разработчики рассчитывают на стандартное разрешение монитора (вроде 1024x768 пикселей) и проектируют свои окна с учетом этого, стараясь обеспечить разумное поведение при изменении размеров в большую и меньшую сторону.

Проблема в том, что пользовательский интерфейс в традиционных Windows-приложениях не является масштабируемым. В результате, если вы используете монитор с высоким разрешением, который располагает пиксели более плотно, окно приложения становится меньше и читать текст в нем труднее. Эта проблема особенно актуальна для новых мониторов, которые имеют высокую плотность пикселей и соответственно работают с более высоким разрешением.

Например, легче встретить мониторы (особенно на портативных компьютерах), которые имеют плотность пикселей в 120 dpi или 144 dpi (точек на дюйм), чем более традиционные 96 dpi. При их встроенном разрешении эти мониторы располагают пиксели более плотно, создавая напрягающие глаз мелкие элементы управления и текст. В идеале приложения должны использовать более высокую плотность пикселей, чтобы отобразить больше деталей. Например, монитор с высоким разрешением может отображать одинакового размера значки панели инструментов, но использовать дополнительные пиксели для отображения мелкой графики. Подобным образом можно сохранить некоторую базовую компоновку, но обеспечить более высокую четкость деталей.

По разным причинам такое решение было невозможно в прошлом. Хотя можно изменять размер графического содержимого, нарисованного в GDI/GDI+, компонент User32 (который генерирует визуальное представление распространенных элементов управления) не поддерживает реального масштабирования.

WPF не страдает от этой проблемы, потому что самостоятельно визуализирует все элементы пользовательского интерфейса — от простых фигур до таких распространенных элементов управления, как кнопки. В результате если вы создаете кнопку шириной в 1 дюйм на обычном мониторе, она останется шириной в 1 дюйм и на мониторе с высоким разрешением. WPF просто визуализирует ее более детализировано, с большим количеством пикселей.

Так выглядит картина в целом, но нужно уточнить еще несколько деталей. Самое важное, что следует осознать — WPF базирует свое масштабирование на системной установке DPI, а не на DPI физического дисплейного устройства. Это совершенно логично — в конце концов, если вы отображаете приложение на 100-дюймовом проекторе, то, скорее всего, отойдете подальше на несколько шагов и будете ожидать увидеть огромную версию окон. Конечно, не желательно, чтобы WPF масштабировал приложение, уменьшая его до "нормального" размера. Аналогично, если вы используете портативный компьютер с дисплеем высокого разрешения, то хотите увидеть несколько уменьшенные окна; это цена, которую приходится платить за то, чтобы уместить всю информацию на маленьком экране. Более того, у разных пользователей разные предпочтения на этот счет. Некоторым нужны расширенные подробности, в то время как другие хотят увидеть больше содержимого.

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

Единицы WPF

Окно WPF и все элементы внутри него измеряются в независимых от устройства единицах. Такая единица определена как 1 /96 дюйма. Чтобы понять, что это означает на практике, нужно рассмотреть пример.

Предположим, что вы создаете в WPF маленькую кнопку размером 96x96 единиц. Если вы используете стандартную установку Windows DPI (96 dpi), то каждая независимая от устройства единица измерения соответствует одному реальному физическому пикселю. Это потому, что WPF использует следующее вычисление:

[Размер в физических единицах] = [Размер в независимых от устройства единицах] х [DPI системы] =
= 1/96 дюйма х 96 dpi = 1 пиксель

По сути, WPF предполагает, что ему нужно 96 пикселей, чтобы отобразить один дюйм, потому что Windows сообщает ему об этом через системную настройку DPI. Однако в действительности это зависит от применяемого дисплейного устройства. Например, рассмотрим 20-дюймовый жидкокристаллический монитор с максимальным разрешением в 1600x1200 пикселей. Используя теорему Пифагора, вы можете вычислить плотность пикселей для этого монитора, как показано ниже:

[DPI экрана] = (1600^2 + 1200^2)^0.5 пикселей / 20 дюймов = 100 dpi

В этом случае плотность пикселей составляет 100 dpi — немного больше того, что предполагает Windows. В результате на этом мониторе кнопка размером 96x96 пикселей будет несколько меньше одного дюйма.

С другой стороны, рассмотрим 15-дюймовый жидкокристаллический монитор с разрешением 1024x768 пикселей. Здесь плотность пикселей составит около 85 dpi, поэтому кнопка размером 96x96 пикселей окажется размером немного больше 1 дюйма.

В обоих случаях, если вы уменьшите размер экрана (скажем, переключившись на разрешение 800x600), то кнопка (и любой другой экранный элемент) станет пропорционально больше. Причина в том, что системная установка DPI останется 96 dpi. Другими словами, Windows продолжает предполагать, что 96 пикселей составляют дюйм, несмотря на то, что при меньшем разрешении потребуется существенно меньше пикселей.

Возможно, вам известно, что жидкокристаллические мониторы создаются с единственным разрешением, которое называется естественным разрешением. При более низком разрешении монитору приходится использовать интерполяцию, чтобы заполнить лишние пиксели, это может вызвать нерезкость. Чтобы получить наилучшее качество изображения, всегда лучше использовать естественное разрешение. Если хотите иметь более крупные окна, кнопки и текст, рассмотрите вместо этого возможность модификации системной установки DPI.

Системная установка DPI

До сих пор пример кнопки WPF работал точно так же, как любой другой интерфейсный элемент в Windows-приложении любого иного типа. Отличие проявляется при изменении вашей системной установки DPI. В предыдущем поколении Windows это средство иногда называли крупными шрифтами. Это потому, что системная установка DPI влияет на размер системных шрифтов, часто оставляя прочие детали неизменными.

Многие Windows-приложения не полностью поддерживают увеличенные установки DPI. В худшем случае увеличение системной установки DPI может привести к появлению окон, в которых некоторое содержимое увеличено, а другое — нет, что может привести к утере части содержимого или даже к нефункциональным окнам.

Здесь поведение WPF отличается. WPF воспринимает системную установку DPI естественным образом и без особых затрат. Например, если вы измените системную установку DPI на 120 dpi (распространенный выбор пользователей экранов с большим разрешением), WPF предполагает, что для заполнения дюйма пространства нужно 120 пикселей. WPF использует следующее вычисление для определения того, как он должен транслировать логические единицы в физические пиксели устройства:

[Размер в физических единицах] = [Размер в независимых от устройства единицах] х [DPI системы] =
= 1/96 дюйма х 120 dpi =1,25 пикселя

Другими словами, когда вы устанавливаете системную настройку DPI в 120 dpi, то механизм визуализации WPF предполагает, что одна независимая от устройства единица измерения соответствует 1,25 пикселя. Если вы отображаете кнопку 96x96, то физический ее размер составит 120x120 пикселей (потому что 96 х 1,25 = 120). Именно такого результата вы и ожидаете — кнопка размером в 1 дюйм имеет такой же размер на мониторе с повышенной плотностью пикселей.

Такое автоматическое масштабирование было бы не слишком полезным, если бы касалось только кнопок. Но WPF использует независимые от устройства единицы для всего, что отображает, включая фигуры, элементы управления, текст и любые другие ингредиенты, которые помещаются в окно. В результате можно изменять системную установку DPI, как вам заблагорассудится, и WPF незаметно подгонит размеры окон приложения.

В зависимости от системной установки DPI вычисляемый размер пикселя может быть выражен дробным значением. Можно предположить, что WPF просто округляет все размеры до ближайшего пикселя. Однако по умолчанию WPF поступает несколько иначе. Если грань элементов приходится на точку между пикселями, WPF использует сглаживание, чтобы размыть эту грань. Это может показаться странным решением, но на самом деле оно вполне оправдано. Элементы управления не обязательно должны иметь прямые четкие грани, если для их отображения применяется специально отрисованная графика, поэтому некоторая степень сглаживания все равно необходима.

Лучший чат для C# программистов