共用方式為


UI 自動化和螢幕調整

備註

本檔適用於想要使用 System.Windows.Automation 命名空間中定義的受控UI自動化類別的 .NET Framework 開發人員。 如需 UI 自動化的最新資訊,請參閱 Windows 自動化 API:使用者介面自動化

從 Windows Vista 開始,Windows 可讓使用者變更每英吋點數 (dpi) 設定,讓畫面上的大多數使用者介面 (UI) 元素看起來更大。 雖然此功能早已在 Windows 中提供,但在舊版中,調整必須由應用程式實作。 從 Windows Vista 開始,桌面視窗管理員會針對未處理自己調整的所有應用程式執行預設調整。 使用者介面自動化用戶端應用程式必須將這項功能納入考慮。

在 Windows Vista 中進行比例調整

默認 DPI 設定為 96,表示 96 像素佔用寬度或高度 1 英吋。 「英吋」的確切量值取決於監視器的大小和實體解析度。 例如,在 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自動化用戶端中縮放

UI 自動化 API 不會使用邏輯座標。 下列方法和屬性會傳回實體座標,或採用它們做為參數。

根據預設,在非 96 DPI 環境中執行的 UI 自動化用戶端應用程式將無法從這些方法和屬性取得正確的結果。 例如,由於游標位置在邏輯座標中,客戶端不能直接傳遞這些座標到 FromPoint 以取得游標下的元素。 此外,應用程式將無法正確將視窗放在其工作區外。

解決方案分為兩個部分。

  1. 首先,讓用戶端應用程式支援 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 感知,這表示屬於該程序的所有視窗都不經過縮放。 例如,在 醒目提示器範例中,組成醒目提示矩形的四個視窗位於從 UI 自動化取得的實體座標,而不是邏輯座標。 如果樣本不是 dpi 感知,則會在桌面上的邏輯座標上繪製反白顯示,這會導致非 96 DPI 環境中的放置不正確。

  2. 若要取得游標座標,請呼叫 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 函數 PhysicalToLogicalPointLogicalToPhysicalPoint 在邏輯座標和實體座標之間進行轉換。

另請參閱