Поддержка WPF ClickOnce DPI для каждого монитора v2

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

Итак, вопрос: как включить поддержку DPI для каждого монитора v2 в сценарии ClickOnce (в частности, WPF, C#)?

Per-Monitor v2 был добавлен в обновлении Win 10 Creators Update (1703). ClickOnce, как известно, не поддерживает осведомленность о DPI, объявленную в файле app.manifest, как здесь.


person Marko    schedule 21.04.2017    source источник


Ответы (3)


Прежде всего, любой, кто хочет кричать, просто нацельтесь на .NET 4.6.2, по умолчанию включена поддержка DPI для каждого монитора — это просто неправда.
То, что по умолчанию включено в .NET 4.6.2, — это шаблонный код за кулисами - довольно неприятные перехватчики С++ в коде объявления окна, чтобы включить поддержку осведомленности о dpi для каждого монитора. это.
(обратите внимание, что более ранние версии .NET не будут поддерживать учет dpi для каждого монитора, даже с манифестом приложения, если только вы не вручную добавить шаблонный код)

Теперь к ответу:

  1. Убедитесь, что ваш проект ориентирован на .NET 4.6.2. (проще всего использовать для этого Visual Studio 2017)
  2. Отключите поддержку DPI в свойствах сборки. Поверьте мне, мы снова включим его позже в коде. Для этого откройте AssemblyInfo.cs в узле Properties вашего проекта (разверните значок гаечного ключа внутри SolutionExplorer, обычно справа). Добавьте следующий код в самую последнюю строку: [assembly: DisableDpiAwareness]. (для этого потребуется оператор using System.Windows.Media;, просто щелкните лампочку, которая появляется при наведении указателя мыши на красную волнистую линию, и добавьте предлагаемый оператор использования)
  3. Добавьте файл app.manifest и заявите о поддержке Win 10 и других версий ОС. Щелкните правой кнопкой мыши свой проект в SolutionExplorer -> добавить -> новый элемент -> Файл манифеста приложения. Откройте созданный файл манифеста и в середине есть раздел с версиями ОС, раскомментируйте их как таковые: < img src="https://i.stack.imgur.com/31L4m.png" alt="Версии ОС в app.manifest"> Не раскомментируйте раздел о поддержке DPI (чуть ниже)! ClickOnce выдаст ошибки, если вы это сделаете.
  4. Теперь вам понадобится следующий код С# где-то в вашем проекте, я рекомендую создать для него новый статический класс:

    internal static class NativeMethods
    {
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern bool SetProcessDpiAwarenessContext(int dpiFlag);
    
        [DllImport("SHCore.dll", SetLastError = true)]
        internal static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);
    
        [DllImport("user32.dll")]
        internal static extern bool SetProcessDPIAware();
    
        internal enum PROCESS_DPI_AWARENESS
        {
            Process_DPI_Unaware = 0,
            Process_System_DPI_Aware = 1,
            Process_Per_Monitor_DPI_Aware = 2
        }
    
        internal enum DPI_AWARENESS_CONTEXT
        {
            DPI_AWARENESS_CONTEXT_UNAWARE = 16,
            DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17,
            DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18,
            DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34
        }
    }
    
  5. Заключительная часть — это вызов вышеуказанных методов p/invoke и объявление поддержки осведомленности о dpi. Нам нужно сделать это до запуска любого другого кода. Для этого щелкните правой кнопкой мыши app.xaml в SolutionExplorer и выберите View Code. Затем добавьте этот код:

    protected override void OnStartup(StartupEventArgs e)
    {
        if (Environment.OSVersion.Version >= new Version(6, 3, 0)) // win 8.1 added support for per monitor dpi
        {
            if (Environment.OSVersion.Version >= new Version(10, 0, 15063)) // win 10 creators update added support for per monitor v2
            {
                 NativeMethods.SetProcessDpiAwarenessContext((int)NativeMethods.DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
            }
            else NativeMethods.SetProcessDpiAwareness(NativeMethods.PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware);
        }
        else NativeMethods.SetProcessDPIAware();
    
        base.OnStartup(e);
    }
    
  6. Убедитесь, что остальная часть вашего кода правильно обрабатывает DPI. Начиная с .NET 4.6.2 вы можете использовать событие OnDpiChanged и метод VisualTreeHelper.GetDpi().

Наслаждаться :)

person Marko    schedule 21.04.2017
comment
Я попробовал это, и все, кажется, работает, за исключением того, что полосы прокрутки в приложении (scrollviewer) не масштабируются. У вас есть идеи, почему? Если я объявляю о поддержке V2 через манифест, полосы прокрутки выглядят нормально, но, конечно же, развертывание прерывается. Я проверил, что вызов SetProcessDpiAwarenessContext с V2 завершается успешно. (Windows 10, последнее обновление, таргетинг на 4.6.2) - person randomThought; 15.05.2018
comment
это решение работает на windows 7? это работало для окон 10 отлично - person ibr; 02.01.2019

@Марко - отличный ответ! Но вот одна маленькая деталь: если вы смешиваете WinForms с WPF, вам нужно добавить конфигурацию WinForms. Пожалуйста, обновите свой ответ, мне не хватает смысла, чтобы оставить комментарий...

<System.Windows.Forms.ApplicationConfigurationSection>
     <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>

ссылка: https://docs.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms

person Andrey    schedule 25.01.2019

Поддержка DPI объявлена ​​в app.manifest утверждается, что это одна из функций .NET 4.7.2.

person Vlad    schedule 06.02.2018
comment
Win 10 v1803 и таргетинг на .NET 4.7.2 действительно работают со следующим манифестом: <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor, System</dpiAwareness> </windowsSettings> </application> К сожалению, это не работает для Server 2008r2 с установленным .NET 4.7.2 (возможно, и с Win 7), потому что манифест не может быть проанализирован. - person Marko; 18.09.2018