WPF ClickOnce DPI感知Per-Monitor v2

时间:2017-04-21 08:45:14

标签: c# wpf clickonce dpi

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

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

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

3 个答案:

答案 0 :(得分:4)

首先,任何想要喊出来的人 - 只需要针对.NET 4.6.2,默认情况下启用每台显示器DPI感知 - 这根本不是真的。
.NET 4.6.2中默认启用的是幕后的样板代码 - 窗口声明代码中非常讨厌的c ++钩子,以支持每个监视器的dpi感知。
您仍然必须通过app.manifest声明您支持每个显示器dpi感知,但ClickOnce不支持它。
(请注意,早期版本的.NET不支持每个监视器dpi感知,即使使用应用程序清单,除非您manually add the boilerplate code

现在回答:

  1. 确保您的项目定位于 .NET 4.6.2 。 (最简单的方法是使用Visual Studio 2017)
  2. 在程序集属性中禁用DPI感知。相信我,我们将在稍后的代码中重新启用它。为此,请在项目属性节点下打开 AssemblyInfo.cs (在 SolutionExplorer 中展开扳手图标,通常在右侧)。将以下代码添加到最后一行:[assembly: DisableDpiAwareness]。 (这需要using System.Windows.Media;声明,只需点击悬停在红色波浪线上方时出现的灯泡并添加建议的使用声明)
  3. 添加 app.manifest 文件并宣布支持Win 10和其他操作系统版本。在 SolutionExplorer - >中右键单击您的项目。添加 - >新项目 - >应用清单文件。打开创建的清单文件,中间有一个OS版本的部分,取消注释它们: 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()方法。

  7. 享受:)

答案 1 :(得分:1)

@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

答案 2 :(得分:0)

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