c# - WPF ClickOnce DPI 感知 Per-Monitor v2

标签 c# wpf clickonce dpi

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

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

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

最佳答案

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

现在回答:

  1. 确保您的项目面向 .NET 4.6.2。 (为此最容易使用 Visual Studio 2017)
  2. 在程序集属性中禁用 DPI 感知。相信我,我们稍后会在代码中重新启用它。为此,打开项目 Properties 节点下的 AssemblyInfo.cs(展开 SolutionExplorer 中的 Spanner 图标,通常位于右侧)。将以下代码添加到最后一行:[assembly: DisableDpiAwareness]。 (这将需要 using System.Windows.Media; 语句,只需单击将鼠标悬停在红色波浪线上时出现的灯泡并添加建议的 using 语句)
  3. 添加一个 app.manifest 文件并声明对 Win 10 和其他操作系统版本的支持。在 SolutionExplorer 中右键单击您的项目 -> 添加 -> 新项目 -> 应用程序 list 文件。打开创建的 list 文件,中间有一个包含操作系统版本的部分,取消注释: OS versions in 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
        }
    }
    
  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);
    }
    
  6. 确保您的其余代码能够正确处理 DPI。从 .NET 4.6.2 开始,您可以使用 OnDpiChanged 事件和 VisualTreeHelper.GetDpi() 方法。

享受 :)

关于c# - WPF ClickOnce DPI 感知 Per-Monitor v2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43537990/

相关文章:

c# - 查找用户管理的所有组

c# - 来自 Silverlight 应用程序的 WCF 自定义身份验证

c# - Storyboard /动画的 "handful"影响性能?

wcf - 忽略一次点击登录页面的客户端证书弹出窗口 - 但仍需要证书才能获得服务

c# - 如何使用 C# 自动化 SAP GUI

c# - 流利的 NHibernate,实现 ISQLExceptionConverter 的正确方法

wpf - 确定 xamDataGrid 上的筛选器状态

wpf - 如何设置 WPF ListView 选定项颜色?

clickonce - 是否可以将NGen与ClickOnce部署一起使用?

visual-studio - 如何在 ClickOnce 部署中自动重命名 "Application Files"文件夹