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

Масштабирование неклиентской области (строка заголовка, строка меню) для поддержки высокого уровня DPI для каждого монитора

В Windows 8.1 появилась возможность иметь разные настройки DPI для разных мониторов. Эта функция известна как "поддержка на высоком уровне DPI для каждого монитора". Он сохраняется и был усовершенствован в Windows 10.

Если приложение не выбирает (т.е. не знает DPI или не знает о высоком DPI), он автоматически масштабируется DWM до соответствующего DPI. Большинство приложений относятся к одной из этих двух категорий, включая большинство утилит в комплекте с Windows (например, Блокнот). В моей тестовой системе монитор с высоким разрешением DPI установлен на 150% шкалы (144 DPI), в то время как обычный монитор установлен на системный DPI (100% шкала, 96 DPI). Поэтому, когда вы открываете одно из этих приложений на экране с высоким DPI (или перетаскиваете его там), виртуализация запускается, увеличивая все, но также делая ее невероятно размытой.

title=

С другой стороны, если приложение явно указывает, что оно поддерживает высоко-DPI для каждого монитора, то виртуализация не выполняется, и разработчик отвечает за масштабирование. Microsoft имеет довольно полное объяснение здесь * но в интересах автономного вопроса я буду подведем итог. Во-первых, вы указываете поддержку, установив <dpiAware>True/PM</dpiAware> в манифесте. Это позволяет вам получать WM_DPICHANGED сообщения, в котором указывается как новый параметр DPI, так и предлагаемый новый размер и положение для вашего окна, Он также позволяет вам вызвать функцию GetDpiForMonitor и получить фактический DPI, не подозревая о причине совместимости. Кенни Керр также написал всеобъемлющий учебник.

Я получил все это успешно в небольшом тестовом приложении на С++. Это много шаблонов и в основном настроек проекта, поэтому я не вижу большого смысла в публикации полного примера здесь. Если вы хотите проверить это, либо следуйте инструкциям Kenny, этот учебник по MSDN, или скачайте официальный образец SDK. Теперь текст в клиентской области выглядит хорошо (из-за моей обработки WM_DPICHANGED), но поскольку виртуализация больше не выполняется, масштабирование неклиентской области отсутствует. В результате строка заголовка/заголовка и строка меню имеют неправильный размер - они не становятся больше на экране с высоким разрешением DPI:

title=

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

было предложено , что ответа нет - ваш единственный выбор - сделать индивидуальное рисование всего окна, в том числе и неклиентской. Хотя это, безусловно, возможно, и действительно то, что делают приложения UWP (ранее известные как Metro), такие как калькулятор Windows 10, не является работоспособным вариантом для настольных приложений, которые используют многие неклиентские виджеты и надеются выглядеть родными.

Кроме того, это явно ложно. Выбранные по умолчанию заголовки не могут быть единственным способом получения правильного поведения, поскольку команда оболочки Windows сделала это. Смиренный диалог запуска ведет себя точно так, как ожидалось, правильно изменяя размеры как клиентской, так и неклиентской областей при перетаскивании между мониторами с разными DPI:

title=

Исследование с помощью Spy ++ подтверждает, что это просто стандартное диалоговое окно Win32 в стиле болота - ничего необычного. Все элементы управления являются стандартными элементами управления Win32 SDK. Это не приложение UWP, и они не набросали строку заголовка - он по-прежнему имеет стиль WS_CAPTION. Он запускается с помощью процесса explorer.exe, который помечен как один из них для мониторинга с высоким уровнем DPI (проверен с помощью Process Explorer и GetProcessDpiAwareness). Это сообщение в блогеподтверждает, что и диалог "Запуск", и "Командная строка" были перезаписаны в Windows 10 для правильной масштабирования (см. "Командные оболочки и др." ). Что такое диалог "Запуск", чтобы изменить размер заголовка?

Common Dialog API, отвечающий за диалоги открытия и сохранения нового стиля, также правильно масштабируется при запуске из процесса, который за -monitor high-DPI, как вы можете видеть, нажав кнопку "Обзор" в диалоговом окне "Выполнить". То же самое для Task Dialog API, создавая странную ситуацию, когда приложение запускает диалоговое окно с заголовком другого размера. (Однако устаревший API MessageBox не был обновлен и демонстрирует то же поведение, что и тестовое приложение.)

Если команда оболочки делает это, это должно быть возможно. Я просто не могу себе представить, что команда, ответственная за проектирование/внедрение поддержки DPI на мониторе, пренебрегла тем, чтобы обеспечить разумный способ для разработчиков производить совместимые приложения. Такие функции требуют поддержки разработчика, либо они разбиты из коробки. Даже приложения WPF нарушены - Microsoft Проект Perware Monitor Aware WPF не может масштабировать область без клиента, в результате чего заголовок, который является неправильный размер. Я не очень люблю теории заговора, но это пахнет маркетинговым движением, чтобы препятствовать разработке настольных приложений. Если это так, и нет официального пути, я буду принимать ответы, которые полагаются на недокументированное поведение.

Говоря о недокументированном поведении, сообщения окна журнала, когда диалог запуска перетаскивается между мониторами с разными настройками DPI, показывают, что он получает недокументированное сообщение 0x02E1. Это несколько интересно, потому что этот идентификатор сообщения точно один больше, чем документированное сообщение WM_DPICHANGED (0x02E0). Однако мое тестовое приложение никогда не получает это сообщение, независимо от его настроек DPI. (Любопытно, что при закрытой проверке обнаруживается, что Windows немного увеличивает размер глифов minimize/maximize/close в строке заголовка, когда окно перемещается на монитор с высоким DPI. Они все еще не такие большие, как при виртуализации, но они немного больше, чем глифы, которые он использует для немасштабированных приложений системного DPI.)

До сих пор моя лучшая идея заключалась в том, чтобы обрабатывать сообщение WM_NCCALCSIZE для настройки размера неклиентской области. Используя флаг SWP_FRAMECHANGED с SetWindowPos function, я могу заставить окно изменять размер и перерисовывать свою неклиентскую область в ответ до WM_DPICHANGED. Это прекрасно работает, чтобы уменьшить высоту заголовка или вообще удалить его, но он никогда не сделает его выше. Подпись, похоже, достигает максимума на высоте, определяемой системой DPI. Даже если бы это сработало, это не было бы идеальным решением, потому что это не помогло бы с помощью панели меню или полос прокрутки & hellip, но по крайней мере это было бы началом. Другие идеи?

* Я знаю, что в этой статье говорится "Обратите внимание, что неклиентская область для каждого приложения, поддерживающего мониторинг-DPI, не является масштабируется Windows и будет казаться пропорционально меньшим на высоком дисплее DPI" . См. выше, почему это (1) неверно и (2) неудовлетворительно. Я ищу обходное решение, отличное от пользовательского рисования неклиентской области.

4b9b3361

Ответ 1

В любых современных сборках Windows Insider (build >= 14342, SDK version # >= 14332) существует API EnableNonClientDpiScaling (который принимает HWND в качестве аргумента), который позволит масштабировать неклиентский DPI для верхнего уровень HWND. Эта функциональность требует, чтобы окно верхнего уровня работало в режиме распознавания DPI на мониторе. Этот API должен вызываться из обработчика WM_NCCREATE для окна. Когда этот API вызывается в окне верхнего уровня, его панель заголовка, полосы прокрутки верхнего уровня, системное меню и меню будут масштабироваться по шкале DPI при изменении DPI приложений (это может произойти, когда приложение перемещается на дисплей с другим масштабированием дисплея или когда DPI изменяется по другим причинам, таким как пользователь, изменяющий настройки, или когда RDP-соединение изменяет масштабный коэффициент).

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

Насколько я знаю, нет никакого способа масштабирования шкалы DPI без клиента без этого API.

Обратите внимание, что этот API еще не завершен, и он может измениться до выпуска в обновлении Windows 10 Anniversary. Следите за MSDN для официальной документации, когда она станет окончательной.

Ответ 2

Благодаря поддержке Per Monitor V2 DPI в Windows 10 Creators Update (сборка 15063) вы можете легко решить эту проблему без EnableNonClientDpiScaling.

Чтобы включить поддержку Per Monitor V2 DPI и при этом поддерживать старую поддержку Per Monitor DPI в более старых сборках Windows 10 и Windows 8.1 и DPI в более старых версиях Windows, сделайте свое приложение таким:

<assembly ...>
    <!-- ... --->
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>True/PM</dpiAware>
            <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>

Рекомендации: