【譯】Windows 10 創作者更新中用於桌面應用的高 DPI 縮放改進

作者:Peter Felts (Microsoft Product Manager)

原文地址:High-DPI Scaling Improvements for Desktop Applications in the Windows 10 Creators Update

術語表

為避免混淆和困惑,以下詞語將不會被翻譯,相關解釋請參考此表或自行查找。

  • HWND: Windows 桌面程序開發里的概念,指窗口句柄。
  • DPI: Dot Per Inch,一種量度單位,形容在每一英寸長度中,取樣或可顯示/輸出的點的數目。
  • DPI Awareness: 應用處理 DPI 的能力級別。
  • System Aware: 應用可以根據系統 DPI 縮放設置處理主屏幕的 DPI 縮放,但是在具有不同 DPI 的屏幕上,由系統完成縮放。
  • Per-Monitor DPI Aware: 應用可以根據每個屏幕自己的 DPI 來處理縮放。
  • Context: 上下文。
  • DPI Awareness mode: 進程範圍內的 DPI 支持模式。
  • DPI Awareness context: Windows 10 年度更新里引入的新概念,指一個線程的 DPI 支持能力,與 DPI Awareness mode 相區分。

譯者其實好久沒說中文了,如果表達奇怪請見諒。

在上一篇關於高 DPI 縮放改進的博客文章中,我們討論了可能導致 Windows 桌面應用在高 DPI 顯示器上顯示模糊或尺寸錯誤的幾種情況。這些情況在將設備連接到擴展底座,從擴展底座斷開,或是使用如遠程桌面協議n(RDP) 之類的遠程技術時表現得頗為顯著。

在 Windows 10 年度更新中,我們引入了混合模式 DPI 縮放和其他跟 DPI 有關的 API 來處理這些問題。這些 API 使得開發者處理動態 DPI 場景(桌面程序運行時探測和處理 DPI 變更)變得更加便捷。我們仍舊在為了改善高 DPI 場景而努力。在這篇文章中,我們將快速瀏覽在即將到來的 Windows 10 創作者更新里的高 DPI 改進。在開始闡述改進之前,讓我們來回顧一下問題:

上面這張圖展示了數種在 Windows 10 里使用多顯示器多 DPI 時會遇到的問題。在這個例子中,一個低 DPI 顯示器(主顯示器)連接到一個高 DPI 外部顯示器。在這張圖,你看到了:

  1. 一些程序(如 Word)在高 DPI 顯示器上顯示模糊
  2. 一些程序(如 PowerPoint 和 Skype for Business)顯示清晰但是尺寸錯誤
  3. 桌面程序圖標尺寸在高 DPI 顯示器上不正確
  4. 工具提示的尺寸不正確
  5. 桌面水印的尺寸不正確

請注意這只是非常多的問題中的一部分。當顯示拓撲反過來(比如高 DPI 顯示器連接到低 DPI 顯示器)時,在這張圖裡太小的內容可能會變得太大。劇透警告:很多(但不是全部)問題在創造者更新中已經解決。

在創造者更新中,面向開發者的改進

創造者更新中的高 DPI 改進可以分為兩類:

  • 面向桌面應用開發者的改進
  • 面向終端用戶的改進

我們首先來討論開發者關注的改進。如果 Microsoft 想要成功地減少終端用戶看到模糊或尺寸不正確的桌面應用的機會,開發者妥善處理動態nDPI 縮放的方法就應該儘可能簡單。我們通過為桌面程序框架逐漸添加自動按屏幕nDPI 完成的縮放來慢慢接近這個目標。以下是在創造者更新中的改進:

Per-monitor DPI Awareness V2

年度更新里引入的混合nDPI 縮放的概念可以讓開發者對進程內的每個頂級窗口使用不同的 DPI Awareness context。這些上下文可以和進程範圍內的默認設定不一致。這將使得您可以只關注用戶界面中最重要的內容,並讓nWindows 來處理其他窗口的點陣圖縮放來適配 Per-monitor DPI aware。在創作者更新中,我們添加了新的 DPI Awareness context,DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,也就是 Per-monitor DPI awareness V2。

技術上說,PMv2 是一個 DPI_AWARENESS_CONTEXT,不是進程範圍內通過 PROCESS_DPI_AWARENESSn定義的 DPI awareness mode。PMv2 填補了原版 Per-monitor DPI Awareness 缺失的功能。這個上下文賦予應用這些能力:

子窗口的 DPI 更改通知

當頂級窗口或者進程在 PMv1 模式運行時,只有頂級窗口會收到 DPI 更改通知,而通知變更到子窗口需要您自己負責。在 PMv2 中,所有在同一個 HWND 樹中的子窗口 HWND 會以從下到上,然後從上到下的方式被通知更改:

  • 消息 WM_DPICHANGED_BEFOREPARENT (從下到上)
  • 消息 WM_DPICHANGED_AFTERPARENT(從上到下)

參數 WPARAMLPARARM 在這兩個消息中不使用,將始終為零值。它們僅僅通知您的子窗口 HWND DPI 更改正在發生/已經發生。

對非客戶端區域的縮放

在年度更新之前,沒有辦法對 Windows 繪製的非客戶端區域(比如窗口標題欄、系統菜單、頂級滾動條、菜單欄等)進行nDPI 縮放。這意味著您創建的 Per-monitor DPI Aware 程序在 DPI 發生變更後,在您不自己繪製所有窗口內容的前提下,非客戶區域的尺寸會變得不正確(太大或者太小)。在年度更新中,我們引入了啟用非客戶區域縮放的方法,EnableNonClientDpiScaling。在 PMv2 中, 非客戶區域縮放默認啟用。

對話框的自動縮放

Win32 對話框(使用 CreateDialog* 函數創建的,基於模板的對話框)在創造者更新前不會被自動縮放。 在 PMv2 中,這些對話框會在 DPI 變更時被自動縮放。如果您的對話框布局是由您自己的代碼控制的,您需要確保您的代碼不和nWindows 完成的縮放衝突。您可以使用 SetDialogControlDpiChangeBehavior 和/或 SetDialogDpiChangeBehavior API 來控制 Windows 處理您的控制項或是整個對話框的行為。

細粒度控制對話框縮放

有些時候您希望控制 Windows 縮放對話框或是整個對話框子 HWND 的方法。您可以分別使用 SetDialogDpiChangeBehaviorSetDialogControlDpiChangeBehavior 來防止nWindows 對其或者其內的 HWND 進行縮放。

typedef enum DIALOG_DPI_CHANGE_BEHAVIORS {n DDC_DEFAULT = 0x0000,n DDC_DISABLE_ALL = 0x0001,n DDC_DISABLE_RESIZE = 0x0002,n DDC_DISABLE_CONTROL_RELAYOUT = 0x0004,n} DIALOG_DPI_CHANGE_BEHAVIORS;n nBOOL WINAPI SetDialogDpiChangeBehavior(HWND hDlg, DIALOG_DPI_CHANGE_BEHAVIORS mask, DIALOG_DPI_CHANGE_BEHAVIORS values);ntypedef enum DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS {n DCDC_DEFAULT = 0x0000,n DCDC_DISABLE_FONT_UPDATE = 0x0001,n DCDC_DISABLE_RELAYOUT = 0x0002,n} DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS;n nBOOL WINAPI SetDialogControlDpiChangeBehavior(HWND hWnd, DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS mask, DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS values);n

注意:SetDialogControlDpiChangeBehavior 只對 PMv2 對話框內的第一級子內容有效。如果您有一個更複雜的對話框樹,您需要自己處理裡面的 HWND 縮放。

使用這些API,您可以關閉某個對話框中的特定窗口(或整個對話框本身)的 DPI 縮放。當您的對話框被系統縮放時,一個新的字體會被發送到所有對話框中的nHWND。如果您不想這樣,可以使用 SetDialogControlDpiChangeBehavior 來阻止 Windows 這麼做。

您可以分別使用 GetDialogDpiChangeBehaviorGetDialogControlDpiChangeBehaviorn來查詢對話框或者對話框里的 HWND 的縮放情況。

Windows 通用控制項改進

在 PMv1 中,當 DPI 發生變更的時候,您可以對 HWND 進行尺寸變更/重定位,但是如果這些 HWND 使用了 Windows 通用控制項(比如 Pushbuttonn和 Checkbox 等),Windows 並不會為這些按鈕重新繪製點陣圖(因為他們由 UXTheme 繪製,也稱之為「主題繪製的部分」或者「主題部分」) 。這也意味著取決於顯示拓撲的配置,這些點陣圖部分會變得太大或者太小。除了自己繪製點陣圖,您沒有什麼別的好辦法可以處理這個問題。

PMv2n則會以正確的 DPI 繪製這些點陣圖。下面我們將展示在每屏幕nDPI 感知中,PMv1n不正確縮放和 PMv2 正確處理的情景:

PMv1

nn當屏幕從高 DPI 顯示器移動到低 DPI 顯示器時,通用控制項主題部分比預期的要大。

PMv2

nn從高 DPIn顯示器移動到低 DPI 顯示器的通用控制項主題部分的尺寸正確。

指定 DPI awareness 的回落行為

我們推薦您在應用程序清單里指定 DPI awareness。在年度更新里。我們引入了新的 DPI awareness 標記,<dpiAwareness>。使用這個標記,您可以指定進程範圍內的默認 DPI awareness mode/context 的回落行為。這在為橫跨不同 Windowsn版本運行的程序指定 DPI awareness mode 時非常有用。舉個例子,下面這個清單設定:

<dpiAware>True/PM</dpiAware>n<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>n

會在不同版本的 Windowsn上擁有不同的默認行為:

在程序里設定默認 DPI awareness context

SetProcessDpiAwarenessContext(…) 可以讓您在程序里設定默認的 DPInawareness context。SetProcessDpiAwareness 不接受 DPI awareness context 作為參數,所以沒有辦法在程序里指定 PMv2 為您的進程內默認 DPI awareness context。

面向終端用戶的 DPI 縮放改進

除了為開發者所做的改進,我們也讓作為 Windows 使用者的您在使用混合 DPIn環境時感到更加舒適。以下是在創作者更新中,一些面向終端用戶的 DPI 縮放改進:

強制設定 DPI 縮放模式

有些時候,一些 Windowsn桌面應用定義其自身運行在特定的 DPInawareness mode,比如 System DPI Aware 模式,但是因為種種原因您希望它運行在不同的模式下。一種場景是程序在高 DPI 下表現不佳(這在開發者未在最新的硬體上測試程序時非常常見)。在這種情況下,您可能希望程序運行在 DPI Unaware 模式下。儘管這樣會造成應用程序顯示模糊,但是它至少能用了。您可以在如下的程序屬性中啟用這個功能:

您可以指定以下三種模式:

  • 應用:強製程序運行在 Per-monitor DPI Aware 模式下。這個模式也就是以前的「禁用高 DPI 顯示縮放」。
  • 系統:這是 Windows 處理 System DPI Aware 程序的標準做法。Windows 將會在 DPI 變更時拉伸程序繪製的內容。
  • 系統(增強):GDI 縮放(請往下看)

「系統(增強)」 DPI 縮放模式

在我們使得您處理 DPI 縮放儘可能簡單的同時,我們也意識到了有很多程序出於種種原因可能永遠無法得到更新。對於此類程序,我們在思考更好地讓 Windows 處理它們的 DPI 縮放的辦法。在 Windowsn10 創作者更新中,您將看到我們的初步處理成果。

這個在創造者更新中引入的新功能將會讓基於 GDI 的應用程序的文本和其他基元內容在高 DPI 顯示器上顯示得清晰銳利。Windows 現在可以對基於 GDI 的應用程序進行 Per-monitor DPI 縮放。也就是說,這些程序將會在 Windows 10 創作者更新中自動地得到 Per-monitor DPI Aware 能力。請注意這不是萬全之策,這個解決方案有其自身的一些局限性:

  • GDI+ 內容不會得到縮放
  • DirectX 內容不會得到縮放
  • 點陣圖內容不會自動變得清晰
  • 在不逐一嘗試的情況下,終端用戶不可能判斷這個功能對哪些應用起作用

儘管這個方法有諸多局限性,當下,GDI 縮放的表現依舊讓人印象深刻。所以我們為一些內置應用打開了這個功能。Microsoftn管理控制台 (mmc.exe)n在默認情況下會被 GDI 縮放。也就是說,很多 Windows 內置 MMC Snap-in,比如設備管理器,將會從這個功能中受益。以下是一些截圖樣例:

nn一個非 DPI Aware 的程序運行在 SurfacenBook 的高 DPIn顯示器上(200% DPI 縮放)

同樣的程序,打開了 GDIn縮放(「系統(增強)」 DPI 縮放)

請注意, 在 GDI 縮放版本的應用程序里,儘管點陣圖是模糊的(因為點陣圖是從低 DPI 被拉伸上來的),文字顯示得清晰銳利。

對於非 Windows 自帶應用程序,這個功能可以在程序屬性里的兼容性選項卡里,設定「覆蓋高 DPI 縮放行為」,選定縮放通過「系統(增強)」來啟用。請注意,兼容性選項卡對於隨 Windows 發行的 Microsoft 應用程序不可用。

Internet Explorer

Per-monitor DPI Aware 從 Windows 8.1 開始引入,但是很多 Windows 自帶應用程序也沒有很好地適配這個 DPI 縮放模式,其中一個最典型的例子是 Internet Explorer。在創作者更新中,Internet Explorer 已經被更新以適配動態 DPI 縮放。

在創作者更新之前,如果您將 InternetnExplorer 移動到一個具有不同 DPI 的顯示器,或者顯示器的 DPI 發生了變更(比如連接/斷開到擴展底座、更改設置、遠程桌面連接等),網頁內容將會被正確地進行縮放,但是窗口框架不會。在下圖中,我們將在一個 DPI 縮放級別為 100% 的第二顯示器上,邊對邊展示 Internet Explorer 和 Microsoft Edge 的顯示情況。我們在主顯示器使用高 DPI 縮放級別,然後把窗口移動到低 DPI 的第二顯示器上。您將會注意到 Microsoft Edge 的 UI 正確縮小,但是 InternetnExplorer 的邊框依舊按照原來的縮放比例顯示。

注意:

  • 導航欄的文本顯得非常巨大
  • 導航按鈕很大
  • 豎直滾動條很大
  • 最小化/最大化/關閉按鈕按正確尺寸渲染,但是按鈕間距像是在高 DPI 屏幕上的那樣

在創作者更新中,情況如下圖所示。

窗口圖標

我們聽到的關於混合 DPI 的最大抱怨之一(也是我們遇到的一種)是在「擴展」顯示模式下,桌面圖標大小不會根據每個顯示器的 DPI/縮放比例進行調整。創作者更新修復了這個問題。下圖是創作者更新之前的情況:

這是您將會在創作者更新中看到的情況:

Office 的尺寸問題

儘管這和創作者更新沒有直接關係,最新的 Office 2016 更新已經解決了 Skypenfor Business 和 PowerPointn在混合 DPI 縮放配置下的尺寸問題。

我們還沒有達成的

我們希望作為開發者和終端用戶的您對這些改進感到滿意。儘管我們完成了很多顯著的工作,說實話,我們還有很多事情完成。以下是我們仍然需要完成的一些事情:

高 DPI 開發者文檔

在撰寫這篇博客文章的同時,MSDN上有關高 DPI 的文檔時效性非常糟糕。有關編寫支持 Per-monitor DPI Awareness 應用的文章是在 Windows 8.1 那時撰寫的,且至今為止沒有得到顯著更新。除此之外,很多有關 DPI 縮放/Awareness 的 Windows API 還沒有文檔。一些 Windows API 在 System API Aware 和 Per-monitor API Aware 時的行為是不同的。我們需要清理現有的文檔,然後撰寫新的文檔,以不然您在更新應用時像猜謎一樣猜測這些 API 的行為。我們正在進行相關工作,所以請關注原文以獲得更多此方面的改進。

「魔術數字」

有些時候 Windows 在縮放到高 DPI 時的表現並不是那麼盡如人意。原因很簡單,藏匿在代碼里的「魔術數字」推斷這個世界永遠在 96 DPI 上運作。在以前,這些「魔術數字」並沒有引發很大的問題。隨著 DPI 的緩慢增長,這些問題越來越顯著。我們需要盡我們所能清理這些問題。一些使用了魔術數字的地方包括開始按鈕邊框的寬度和非客戶端區域菜單欄的內距(比如說記事本里的)。在菜單欄這個例子中,隨著 DPI 的增加,您將會注意到每個菜單項目之間的空隙變得越來越小。

子窗口 DPI 縮放

在 Windows 10 年度更新中,我們引入了混合模式 DPI 縮放(又名子進程 DPI 縮放)來使得每個頂級窗口可以擁有不同的 DPI 縮放模式。然而我們沒有提供對子窗口的 DPI 縮放支持。在很多有趣的場景中這很有用——比如您承載了一個外部 HWND,它運行在不同的 DPI Awareness 模式下。如果您不具備有控制創建承載內容的代碼的能力,讓 Windows 來幫您處理DPI縮放是個很好的主意。很不幸,這種場景當前還不受支持。

MFC

儘管我們為 Win32、WPF 和 WinForms 添加了 Per-monitor DPI 縮放支持,MFC 並不原生支持這些功能。

顯示器設置頁面和滑鼠輸入

當您具有多個不同 DPI/縮放比例的顯示器時,顯示器設置頁面會展示您的顯示拓撲。顯示器按解析度展示,我們覺得按照它們在實際環境中的大小展示可能會更好。

如果您有兩個顯示器,他們具有相同尺寸和不同的縮放比例(比如一個是 4K 顯示器,一個是1080p 顯示器),您會在在移動滑鼠的時候遇到一塊滑鼠無法移動的「死亡區域」。因此,讓滑鼠的移動反映出實際世界裡的顯示器尺寸(而不是解析度)會更易於用戶理解。

拖動窗口

當您在兩個具有不同 DPI 的顯示器來回拖動窗口的時候,您會看到一個怪異形狀的窗口。直到您完全拖動大部分窗口到另一個屏幕前,一部分窗口會以錯誤的 DPI 渲染。在這個時候窗口切換到了新的 DPI,而原屏幕上的另一部分顯得怪異。如果在這裡有一些平滑的過渡,可能情況會更好。

需要註銷並重新登錄

切換 DPI 後,需要註銷並重新登錄才能使得多數程序渲染正常是 Windows 的一大痛點。我們當前還沒有為不支持每屏幕 DPI 感知的應用程序們想到解決這個問題的方案。

結論

在文章地最後,我們衷心地希望您對這些改進感到滿意。在改進 Windowsn10 的高 DPI 顯示支持上,我們還有很長的路要走。在 DPI 顯示縮放上,您有什麼意見、建議,或是好的解決方案么?如果有的話,請在原文評論,或是在 Twitter 上 @WindowsUI 。


推薦閱讀:

(windows)微軟Win10 build14997新版本,遊戲模式火力全開
更新手機 App、進軍 PC,智能音箱市場已不能滿足 Alexa 的胃口
利用這款應用,你可以讓開始菜單變得十分「喜+1」
Windows Subsystem for Linux 概述
迅雷又抽風了?Windows 平台上還有這些好用的下載工具

TAG:Windows10 | MicrosoftWindows |