Win10RS1/2/3的DPI支持改进简介
Win10RS1(1607/周年更新):新增一套API,使得每个窗口可以具有不同的DPI缩放兼容性,并大大简化了多显示器DPI编程。WPF可选支持多显示器DPI编程。
Win10RS2(1703/创意者更新):新增PerMonitorV2模式,简化Win32和WinForms的多显示器DPI编程。新增gdiScaling选项,改善DPI虚拟化画质。程序兼容型选项卡新增DPI兼容性设置,允许用户强行打开DPI虚拟化。
Win10RS3(秋季创意者更新,待发布):更改系统级DPI不再需要注销并重新登录,只需重新打开未响应DPI变化的程序即可。
WPF编程
.NET 4.6.2已经支持Win10 RS1的多显示器DPI支持模式,不过,为了使用这个功能,你还需要作以下操作:
1、声明程序支持Win10 RS1的多显示器DPI支持模式,但不支持Win8.1的多显示器DPI支持模式
编写XXXXXXnifest,在assembly标签下添加如下声明:
<code class="language-xml"><application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowssettings> <dpiawareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiawareness> <dpiaware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiaware> </windowssettings> </application> </code>
2、设置目标框架为4.6.2以上,或者在XXXXXXnfig的configuration标签下添加如下声明:
<code class="language-cpp"><runtime> <appcontextswitchoverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false"> </appcontextswitchoverrides></runtime> </code>
Windows Forms编程
Windows Forms已经在.NET Framework 4.7支持Win10 RS2的多显示器DPI第二版。不过,由于Windows Forms的架构限制,实际上有一些bug,比如在频繁调整下,ToolStrip控件会越来越大。
**编写支持多显示器DPI的应用程序,建议使用WPF。**因为WPF基于浮点坐标和矢量绘图,能够更好地支持DPI的动态变化(Windows Forms基于整数坐标和像素绘图)。
如果你想尝试一下这个新功能,可以按照下面的方法声明。
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/dotnet/2017/04/05/announcing-the-net-framework-4-7/
XXXXXXXXXXXXXXXXXXXXXXXXXX/library/XXXXXXXXXXXpx
首先在XXXXXXnifest中允许.NET检测Win10系统版本
<code class="language-xml"><compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <supportedos id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"> </supportedos></application> </compatibility> </code>
然后在XXXXXXnfig中指定目标版本为4.7,并开启Windows Forms的多显示器DPI支持模式第二版
<code class="language-xml"><?xml version="1.0" encoding="utf-8"?> <configuration> <startup> <supportedruntime version="v4.0" sku=".NETFramework,Version=v4.7"> </supportedruntime></startup> <system.windows.forms.applicationconfigurationsection> <add key="DpiAwareness" value="PerMonitorV2"> </add></system.windows.forms.applicationconfigurationsection> </configuration> </code>
GDI缩放技术
Win10 RS2支持GDI缩放技术,可以在大多数情况下取代原有的像素缩放技术,优化一些内容的显示。
XXXXXXnifest声明方法:
<code class="language-xml"><?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestversion="1.0"> <assemblyidentity type="win32" name="MyApp" version="1.0.0.0"> <asmv3:application> <asmv3:windowssettings> <dpiaware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiaware> <gdiscaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiscaling> </asmv3:windowssettings> </asmv3:application> </assemblyidentity></assembly> </code>
Win32编程
Win10RS1添加了一些动态DPI函数,这些函数比Win8.1的那些使用起来更容易。
Win10RS2添加了多显示器DPI支持模式第二版,增加了对话框、菜单栏、非客户区的自动缩放支持,以及子控件的DPI消息通知。
经过实测,其它通用控件可以近乎完美缩放,但是工具栏图标不能自动缩放,还是需要处理一下WM_DPICHANGED,然后重新给工具栏指定一组更大的位图。
不一定要支持Windows 8.1/10TH1/10TH2的多显示器DPI支持模式,甚至连10RS1的都不一定要支持,因为多显示器动态DPI的API是逐渐完善的,而且需要使用多显示器DPI支持的用户一般倾向于升级到最新版Windows 10。
另外要注意,SetProcessDpiAwarenessContext函数,虽然MSDN上写的Win10RS2,实际上在Win10RS1已经导出了,所以如果以-4调用,一定要检查是否返回FALSE,返回FALSE的话需要再以-2调用一次。
XXXXXXinfest多显示器DPI支持模式第二版声明方法:
<code class="language-xml"><application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowssettings> <dpiawareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,System</dpiawareness> <dpiaware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiaware> </windowssettings> </application> </code>
这里的声明取自Windows SDK 10.0.15063.0头文件,而非MSDN Library开发者文档。(2017/4/6更新)
<code class="language-cpp">#pragma once #include <windows.h> #include <uxtheme.h> #define DefineDLLEntry(name) T_##name *name #define LoadDLLEntry(hmod, name) name = (T_##name *)GetProcAddress(hmod, #name) #define _Inout_ #define _In_ #define _Out_ #define _In_opt_ // Windows 7加入的窗口消息 #define WM_DPICHANGED 0x02E0 // Windows 10 RS2加入的窗口消息 #define WM_DPICHANGED_BEFOREPARENT 0x02E2 #define WM_DPICHANGED_AFTERPARENT 0x02E3 #define WM_GETDPISCALEDSIZE 0x02E4 #if NTDDI_VERSION < 0x0A000002 DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); typedef enum DPI_AWARENESS { DPI_AWARENESS_INVALID = -1, DPI_AWARENESS_UNAWARE = 0, DPI_AWARENESS_SYSTEM_AWARE = 1, DPI_AWARENESS_PER_MONITOR_AWARE = 2 } DPI_AWARENESS; #endif #if NTDDI_VERSION < 0x0A000003 typedef enum DIALOG_DPI_CHANGE_BEHAVIORS { DDC_DEFAULT = 0x0000, DDC_DISABLE_ALL = 0x0001, DDC_DISABLE_RESIZE = 0x0002, DDC_DISABLE_CONTROL_RELAYOUT = 0x0004, } DIALOG_DPI_CHANGE_BEHAVIORS; typedef enum DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS { DCDC_DEFAULT = 0x0000, DCDC_DISABLE_FONT_UPDATE = 0x0001, DCDC_DISABLE_RELAYOUT = 0x0002, } DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS; #endif // 不支持DPI #define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1) // 系统级DPI(传统DPI模式) #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2) // 逐显示器DPI第一版(Win8.1/Win10RS1模式) #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3) // 逐显示器DPI第二版(Win10RS2模式) // 支持对话框、菜单、非客户区自动缩放,支持子控件DPI通知 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) // Windows 10 RS1、Windows 10 RS2加入的高DPI支持API struct Win10DPI { typedef BOOL WINAPI T_AdjustWindowRectExForDpi( _Inout_ LPRECT lpRect, _In_ DWORD dwStyle, _In_ BOOL bMenu, _In_ DWORD dwExStyle, _In_ UINT dpi ); typedef BOOL WINAPI T_AreDpiAwarenessContextsEqual( _In_ DPI_AWARENESS_CONTEXT dpiContextA, _In_ DPI_AWARENESS_CONTEXT dpiContextB ); typedef BOOL WINAPI T_EnableNonClientDpiScaling( _In_ HWND hwnd ); typedef DPI_AWARENESS WINAPI T_GetAwarenessFromDpiAwarenessContext( _In_ DPI_AWARENESS_CONTEXT value ); typedef UINT WINAPI T_GetDpiForSystem(void); typedef UINT WINAPI T_GetDpiForWindow( _In_ HWND hwnd ); typedef int WINAPI T_GetSystemMetricsForDpi( _In_ int nIndex, _In_ UINT dpi ); typedef DPI_AWARENESS_CONTEXT WINAPI T_GetThreadDpiAwarenessContext(void); typedef DPI_AWARENESS_CONTEXT WINAPI T_GetWindowDpiAwarenessContext( _In_ HWND hwnd ); typedef BOOL WINAPI T_IsValidDpiAwarenessContext( _In_ DPI_AWARENESS_CONTEXT value ); // Windows 8.1加入,但仍然适用 typedef BOOL WINAPI T_LogicalToPhysicalPointForPerMonitorDPI( _In_ HWND hwnd, _Inout_ LPPOINT lpPoint ); // Windows 8.1加入,但仍然适用 typedef BOOL WINAPI T_PhysicalToLogicalPointForPerMonitorDPI( _In_ HWND hwnd, _Inout_ LPPOINT lpPoint ); typedef DPI_AWARENESS_CONTEXT WINAPI T_SetThreadDpiAwarenessContext( _In_ DPI_AWARENESS_CONTEXT dpiContext ); typedef BOOL WINAPI T_SystemParametersInfoForDpi( _In_ UINT uiAction, _In_ UINT uiParam, _Inout_ PVOID pvParam, _In_ UINT fWinIni, _In_ UINT dpi ); // Windows 10 RS2加入 // 这个其实Windows 10 RS1已经导出了 typedef BOOL WINAPI T_SetProcessDpiAwarenessContext( _In_ DPI_AWARENESS_CONTEXT value ); typedef BOOL WINAPI T_SetDialogDpiChangeBehavior( _In_ HWND hDlg, _In_ DIALOG_DPI_CHANGE_BEHAVIORS mask, _In_ DIALOG_DPI_CHANGE_BEHAVIORS values ); typedef DIALOG_DPI_CHANGE_BEHAVIORS WINAPI T_GetDialogDpiChangeBehavior( _In_ HWND hDlg ); typedef BOOL WINAPI T_SetDialogControlDpiChangeBehavior( _In_ HWND hWnd, _In_ DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS mask, _In_ DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS values ); typedef DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS WINAPI T_GetDialogControlDpiChangeBehavior( _In_ HWND hWnd ); typedef HTHEME WINAPI T_OpenThemeDataForDpi( _In_opt_ HWND hwnd, _In_ LPCWSTR pszClassList, _In_ UINT dpi ); DefineDLLEntry(AdjustWindowRectExForDpi); DefineDLLEntry(AreDpiAwarenessContextsEqual); DefineDLLEntry(EnableNonClientDpiScaling); DefineDLLEntry(GetAwarenessFromDpiAwarenessContext); DefineDLLEntry(GetDpiForSystem); DefineDLLEntry(GetDpiForWindow); DefineDLLEntry(GetSystemMetricsForDpi); DefineDLLEntry(GetThreadDpiAwarenessContext); DefineDLLEntry(GetWindowDpiAwarenessContext); DefineDLLEntry(IsValidDpiAwarenessContext); DefineDLLEntry(LogicalToPhysicalPointForPerMonitorDPI); DefineDLLEntry(PhysicalToLogicalPointForPerMonitorDPI); DefineDLLEntry(SetThreadDpiAwarenessContext); DefineDLLEntry(SystemParametersInfoForDpi); DefineDLLEntry(SetProcessDpiAwarenessContext); DefineDLLEntry(SetDialogDpiChangeBehavior); DefineDLLEntry(GetDialogDpiChangeBehavior); DefineDLLEntry(SetDialogControlDpiChangeBehavior); DefineDLLEntry(GetDialogControlDpiChangeBehavior); Win10DPI() { HMODULE huser32 = GetModuleHandleA("user32"); LoadDLLEntry(huser32, AdjustWindowRectExForDpi); LoadDLLEntry(huser32, AreDpiAwarenessContextsEqual); LoadDLLEntry(huser32, EnableNonClientDpiScaling); LoadDLLEntry(huser32, GetAwarenessFromDpiAwarenessContext); LoadDLLEntry(huser32, GetDpiForSystem); LoadDLLEntry(huser32, GetDpiForWindow); LoadDLLEntry(huser32, GetSystemMetricsForDpi); LoadDLLEntry(huser32, GetThreadDpiAwarenessContext); LoadDLLEntry(huser32, GetWindowDpiAwarenessContext); LoadDLLEntry(huser32, IsValidDpiAwarenessContext); LoadDLLEntry(huser32, LogicalToPhysicalPointForPerMonitorDPI); LoadDLLEntry(huser32, PhysicalToLogicalPointForPerMonitorDPI); LoadDLLEntry(huser32, SetThreadDpiAwarenessContext); LoadDLLEntry(huser32, SystemParametersInfoForDpi); LoadDLLEntry(huser32, SetProcessDpiAwarenessContext); LoadDLLEntry(huser32, SetDialogDpiChangeBehavior); LoadDLLEntry(huser32, GetDialogDpiChangeBehavior); LoadDLLEntry(huser32, SetDialogControlDpiChangeBehavior); LoadDLLEntry(huser32, GetDialogControlDpiChangeBehavior); } }; struct WinVistaDPI { typedef BOOL WINAPI T_SetProcessDPIAware(VOID); typedef BOOL WINAPI T_IsProcessDPIAware(VOID); typedef BOOL WINAPI T_LogicalToPhysicalPoint( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint ); typedef BOOL WINAPI T_PhysicalToLogicalPoint( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint ); DefineDLLEntry(SetProcessDPIAware); DefineDLLEntry(IsProcessDPIAware); DefineDLLEntry(LogicalToPhysicalPoint); DefineDLLEntry(PhysicalToLogicalPoint); WinVistaDPI() { HMODULE huser32 = GetModuleHandleA("user32"); LoadDLLEntry(huser32, SetProcessDPIAware); LoadDLLEntry(huser32, IsProcessDPIAware); LoadDLLEntry(huser32, LogicalToPhysicalPoint); LoadDLLEntry(huser32, PhysicalToLogicalPoint); } }; // Win8.1加入,但已不适用的API #ifdef WIN81DPI_DEPRECATED typedef enum _MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } Monitor_DPI_Type; typedef enum _PROCESS_DPI_AWARENESS { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; typedef enum _SHELL_UI_COMPONENT { SHELL_UI_COMPONENT_TASKBARS = 0, SHELL_UI_COMPONENT_NOTIFICATIONAREA = 1, SHELL_UI_COMPONENT_DESKBAND = 2 } SHELL_UI_COMPONENT; typedef HRESULT WINAPI T_GetDpiForMonitor( _In_ HMONITOR hmonitor, _In_ MONITOR_DPI_TYPE dpiType, _Out_ UINT *dpiX, _Out_ UINT *dpiY ); typedef UINT WINAPI T_GetDpiForShellUiComponent( _In_ SHELL_UI_COMPONENT component ); typedef HRESULT WINAPI T_GetProcessDpiAwareness( _In_ HANDLE hprocess, _Out_ PROCESS_DPI_AWARENESS *value ); typedef HRESULT WINAPI T_SetProcessDpiAwareness( _In_ PROCESS_DPI_AWARENESS value ); #endif </uxtheme.h></windows.h></code>
(未完待续)
[修改于 7年5个月前 - 2017/07/18 22:22:09]
时段 | 个数 |
---|---|
{{f.startingTime}}点 - {{f.endTime}}点 | {{f.fileCount}} |