WPF ClickOnce DPI 感知 Per-Monitor v2

WPF ClickOnce DPI awareness Per-Monitor v2

我打算自己设置这个问题并自己回答,以便其他人可以更轻松地搜索并找到正确答案。我不得不 google 几个小时并从多个来源编译最终结果...

所以问题是 - 如何在 ClickOnce 场景(WPF,特别是 c#)中启用 Per-Monitor v2 DPI 感知?

Win 10 Creators Update (1703) 添加了 Per-Monitor v2。众所周知,ClickOnce 不支持在 app.manifest 文件 .

中声明的 DPI 感知

首先,任何想要大声疾呼的人 - 只需以 .NET 4.6.2 为目标,默认情况下启用每个显示器的 DPI 感知 - 这根本不是真的。
.NET 4.6.2 中默认启用的是幕后的样板代码 - window 声明代码中相当讨厌的 c++ 挂钩,以启用对每个显示器 dpi 感知的支持。
您仍然必须声明您通过 app.manifest 支持每个显示器的 dpi 感知,但 ClickOnce 不支持它。
(请注意,早期版本的 .NET 不支持每个显示器的 dpi 感知,即使有应用程序清单,除非你manually add the boilerplate code

现在回答:

  1. 确保您的项目面向 .NET 4.6.2。 (为此最容易使用 Visual Studio 2017)
  2. 在程序集属性中禁用 DPI 感知。相信我,我们稍后会在代码中重新启用它。为此,请打开项目 Properties 节点下的 AssemblyInfo.cs(展开 SolutionExplorer,通常在右侧)。将以下代码添加到最后一行:[assembly: DisableDpiAwareness]。 (这将需要 using System.Windows.Media; 语句,只需单击将鼠标悬停在红色波浪线上时出现的灯泡并添加建议的 using 语句)
  3. 添加 app.manifest 文件并声明支持 Win 10 和其他 OS 版本。在 SolutionExplorer 中右键单击您的项目 -> 添加 -> 新项目 -> 应用程序清单文件。打开创建的清单文件,中间有一个包含 OS 版本的部分,取消注释: 不要 取消注释有关 DPI 感知的部分(再往下一点)!如果您这样做,ClickOnce 将抛出错误。
  4. 您现在需要在项目中的某处使用以下 C# 代码,我建议为此创建一个新的静态代码 class:

    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 感知支持。我们需要在任何其他代码运行之前执行此操作。为此,在 SolutionExplorer 和 select View Code 中右键单击 app.xaml。然后添加此代码:

    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() 方法。

尽情享受吧:)

在 app.manifest is claimed to be .NET 4.7.2 功能之一中声明了 DPI 感知。

@Marko - 很好的答案!但这里有一个小细节:如果您将 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