文号
t79547
阅读
4020
回复
0
收藏
1
让WPF支持Win8.1/10的多显示器DPI特性(预览版)
2016/04/10
IP:四川
Win8.1/10引入了多显示器DPI特性,允许对不同显示器指定不同DPI,窗口拖到不同DPI的显示器时,会通知程序动态进行缩放。但是目前为止,WPF的稳定版是不支持Win8.1/10的这种特性的。为了支持这个特性,必须安装最新版.NET框架4.6.2 Preview。

对于低于4.6.2版本的编译,默认是不响应DPI变化的,开启DPI响应需要修改app.config,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    
    <appcontextswitchoverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false">
  </appcontextswitchoverrides></runtime>
  <startup>
    <supportedruntime version="v4.0.30319">
  </supportedruntime></startup>
</configuration>

以上只是开启了WPF内部的DPI响应支持。在目前的4.6.2 Preview版本中,默认DPI级别还是等于1(支持系统级DPI),需要手动设置DPI级别为2(支持多显示器DPI)。暂时还没有找到用一般的方法如何改,在这里提供了一个用C#代码调用系统函数修改的方法。

首先在AssemblyInfo.cs中加入这一句,不让WPF自己设置DPI缩放级别:
[assembly: System.Windows.Media.DisableDpiAwareness()]

然后在App.cs中改成如下所示:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Runtime.InteropServices; // DllImport
using System.Diagnostics; // Debug
  
namespace WpfApplication8
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string libname);
        [DllImport("kernel32.dll")]
        private static extern void FreeLibrary(IntPtr hlib);
        [DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
        private static extern IntPtr GetProcAddress(IntPtr hlib, string procname);
        private delegate int ProcSetProcessDpiAwareness(int level);
        private delegate void ProcSetProcessDPIAware();
  
        static void Win7SetDPIAware()
        {
            IntPtr hUser32 = LoadLibrary("user32.dll");
            if (hUser32 != IntPtr.Zero)
            {
                IntPtr addrSetProcessDPIAware = GetProcAddress(hUser32, "SetProcessDPIAware");
                if (addrSetProcessDPIAware != IntPtr.Zero)
                {
                    ProcSetProcessDPIAware SetProcessDPIAware =
                        (ProcSetProcessDPIAware)
                        Marshal.GetDelegateForFunctionPointer(addrSetProcessDPIAware,
                            typeof(ProcSetProcessDPIAware));
                    SetProcessDPIAware();
                }
                FreeLibrary(hUser32);
            }
        }
  
        // 需要在AssemblyInfo.cs里面设置
        // [assembly: System.Windows.Media.DisableDpiAwareness()]
        static bool Win81SetDPIAware()
        {
            IntPtr hShcore = LoadLibrary("shcore.dll");
            if (hShcore != IntPtr.Zero)
            {
                IntPtr addrSetProcessDpiAwareness = GetProcAddress(hShcore, "SetProcessDpiAwareness");
                if (addrSetProcessDpiAwareness != IntPtr.Zero)
                {
                    // Win8.1以上版本,支持多显示器DPI
                    ProcSetProcessDpiAwareness SetProcessDpiAwareness =
                        (ProcSetProcessDpiAwareness)
                        Marshal.GetDelegateForFunctionPointer(addrSetProcessDpiAwareness,
                            typeof(ProcSetProcessDpiAwareness));
  
                    Type t = typeof(Window);
                    var evt = t.GetEvent("DpiChanged");
                    if (evt != null)
                    {
                        // 4.6.2新版WPF,支持DPI响应
                        // 需在app.config里面设置
                        // <runtime>
                        //   <appcontextswitchoverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false">
                        // </appcontextswitchoverrides></runtime>
                        if (SetProcessDpiAwareness(2) < 0)
                        {
                            Debug.WriteLine("SetProcessDpiAwareness(2) failed!");
                        }
                        else
                        {
                            Debug.WriteLine("SetProcessDpiAwareness(2) succeeded!");
                        }
                    }
                    else
                    {
                        // 旧版WPF,不支持DPI响应
                        SetProcessDpiAwareness(1);
                    }
                    FreeLibrary(hShcore);
                    return true;
                }
                else // 找不到SetProcessDpiAwareness
                {
                    FreeLibrary(hShcore);
                    return false;
                }
            }
            else // 找不到shcore.dll
            {
                return false;
            }
        }
  
        static App()
        {
            if (!Win81SetDPIAware())
            {
                Win7SetDPIAware();
            }
        }
    }
}
[ 修改于10年8个月前 - 2016/04/10 19:27:45 ]
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的