WPF ClickOnce DPI感知每个监视器v2

Mar*_*rko 2 c# wpf clickonce dpi

我将设置此问题并亲自回答,以便其他人可以轻松地搜索并找到正确的答案。我不得不谷歌搜索几个小时,并从多个来源汇编最终结果...

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

在Win 10 Creators Update(1703)中添加了Per-monitor v2。众所周知,ClickOnce不支持在app.manifest文件中声明的DPI感知,例如

Mar*_*rko 6

首先,任何想大声疾呼的人-只需针对每个监视器目标.NET 4.6.2,默认情况下就会启用DPI感知-这是不对的。
在.NET 4.6.2中默认情况下启用的是幕后的样板代码-窗口声明代码中非常讨厌的c ++钩子可启用对每个显示器dpi的感知支持。
您仍然必须通过app.manifest声明您支持每个显示器的dpi感知,但ClickOnce不支持。
(请注意,除非您手动添加样板代码,否则即使使用应用程序清单,.NET的早期版本也不支持每个监视器的dpi意识)

现在来回答:

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

    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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 最后一部分是调用上述p / invoke方法并声明dpi感知支持。我们需要在运行任何其他代码之前执行此操作。为此,请在SolutionExplorer中右键单击app.xaml,然后选择。然后添加以下代码: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);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 确保其余代码可以正确处理DPI。从.NET 4.6.2开始,您可以使用OnDpiChanged事件和VisualTreeHelper.GetDpi()方法。

请享用 :)


小智 6

@Marko - 很好的答案!但这里有一个小细节:如果您将 WinForms 与 WPF 混合,则必须添加 WinForms 配置。请更新你的答案,我没有足够的观点发表评论......

<System.Windows.Forms.ApplicationConfigurationSection>
     <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
Run Code Online (Sandbox Code Playgroud)

参考: https: //learn.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms