UI 自動化和畫面縮放比例
注意
本文件適用對象為 .NET Framework 開發人員,其想要使用 System.Windows.Automation 命名空間中定義的受控 UI 自動化類別。 如需 UI 自動化的最新資訊,請參閱 Windows 自動化 API:UI 自動化。
從 Windows Vista 開始,Windows 可讓使用者變更每英吋點數 (DPI) 設定,讓畫面的大多數使用者介面 (UI) 元素看起來更大。 雖然 Windows 在早期已經提供這項功能,但在先前的版本中,縮放比例必須由應用程式來實作。 從 Windows Vista 開始,桌面視窗管理員為所有不處理本身縮放比例的應用程序執行預設的縮放功能。 使用者介面自動化用戶端應用程式必須將這項功能納入考量。
Windows Vista 中的縮放比例
預設的 dpi 設定是 96,表示 96 像素會佔用一英吋的寬度或高度。 「英吋」的確切測量值取決於監視器的大小和實體解析度。 例如,在 12 英吋寬、1280 像素水平解析度的監視器上,96 像素的水平線長度約一英吋的 9/10。
變更 dpi 設定與變更畫面解析度是不同的。 使用 dpi 縮放比例時,畫面的實體像素數目會維持不變。 不過,縮放比例會套用至 UI 元素的大小和位置。 針對未明確要求不縮放的桌面和應用程式,桌面視窗管理員 (DWM) 會自動執行縮放。
實際上,當使用者將縮放因數設為 120 dpi 時,畫面的垂直或水平英吋會增大 25%。 所有維度都會因此調整。 來自畫面上方和左邊緣的應用程式視窗位移會增加 25%。 如果應用程式縮放比例已啟用,但應用程式不會感知 dpi,則視窗大小會以相同的比例增加,同時其所包含的所有 UI 元素的位移和大小也會以相同比例增加。
注意
根據預設,當使用者將 dpi 設為 120 時,DWM 不會針對非 dpi 感知的應用程式執行縮放,但當 dpi設為自訂值 144 或更高時就會執行縮放。 不過,使用者可以覆寫此預設行為。
針對重視畫面座標的應用程式,畫面縮放比例會產生新挑戰。 畫面現在包含兩個座標系統:實體和邏輯。 點的實體座標是來自原點左上方的實際位移 (以像素為單位)。 邏輯座標則是像素本身縮放時,跟著縮放的位移。
假設您設計的對話方塊在座標 (100, 48) 上有一個按鈕。 當這個對話方塊以預設 96 dpi 顯示時,按鈕位在實體座標 (100, 48)。 在 120 dpi 時,它位在實體座標 (125, 60)。 但邏輯座標在任何 dpi 設定時都是相同的:(100, 48)。
邏輯座標很重要,因為不論 dpi 設定為何,它們都會維持作業系統和應用程式的行為一致。 例如, Cursor.Position 一般會傳回邏輯座標。 如果您將游標移至對話方塊內的項目上,不論 dpi 設定為何,系統都會傳回相同的座標。 如果您在 (100, 100) 繪製控制項,系統會將控制項繪製到這些邏輯座標,不論 dpi 設定為何都會佔用相同的相對位置。
使用者介面自動化用戶端中的縮放比例
UI 自動化 API 不會使用邏輯座標。 下列方法和屬性會傳回實體座標或採用它們做為參數。
根據預設,在非 96 dpi 環境中執行的 UI 自動化用戶端應用程式,將無法從這些方法和屬性取得正確的結果。 例如,因為游標位置是在邏輯座標中,用戶端無法單純地將這些座標傳遞至 FromPoint ,以取得游標下的項目。 此外,應用程式也將無法在其用戶端區域之外正確放置視窗。
解決方法分為兩個部分。
首先,讓用戶端應用程式能夠感知 dpi。 若要執行這項動作,請在啟動時呼叫 Win32 語言函式
SetProcessDPIAware
。 以 Managed 程式碼的下列宣告,讓這個函式成為可用的。[System.Runtime.InteropServices.DllImport("user32.dll")] internal static extern bool SetProcessDPIAware();
<System.Runtime.InteropServices.DllImport("user32.dll")> _ Friend Shared Function SetProcessDPIAware() As Boolean End Function
這個語言函式會讓整個處理流程為 dpi 感知,也就是說,屬於該處理程序的所有視窗都沒有縮放。 在 Highlighter Sample 中,構成反白顯示矩型的四個視窗是位在取自 UI 自動化的實體座標,而非邏輯座標。 如果這個範例不是 dpi 感知,系統就會在桌面的邏輯座標上繪製反白顯示,而在非 96 dpi 的環境中,這會造成位置不正確的情況。
若要取得游標座標,請呼叫 Win32 語言函式
GetPhysicalCursorPos
。 下列範例顯示如何宣告及使用這個函式。public struct CursorPoint { public int X; public int Y; } [System.Runtime.InteropServices.DllImport("user32.dll")] internal static extern bool GetPhysicalCursorPos(ref CursorPoint lpPoint); private bool ShowUsage() { CursorPoint cursorPos = new CursorPoint(); try { return GetPhysicalCursorPos(ref cursorPos); } catch (EntryPointNotFoundException) // Not Windows Vista { return false; } }
Structure CursorPoint Public X As Integer Public Y As Integer End Structure <System.Runtime.InteropServices.DllImport("user32.dll")> _ Friend Shared Function GetPhysicalCursorPos(ByRef lpPoint As CursorPoint) As Boolean End Function Private Function ShowUsage() As Boolean Dim cursorPos As New CursorPoint() Try Return GetPhysicalCursorPos(cursorPos) Catch e As EntryPointNotFoundException ' Not Windows Vista Return False End Try End Function
警告
請勿使用 Cursor.Position。 這個屬性在縮放環境中用戶端視窗之外的行為是未定義的。
如果您的應用程式與非 dpi 感知的應用程式執行直接的跨處理序通訊,您可能必須使用 Win32 語言函式 PhysicalToLogicalPoint
和 LogicalToPhysicalPoint
來轉換邏輯與實體座標。