WPF 自訂控制項的 UI 自動化

使用者介面自動化提供一個單一的通用介面,可讓自動化用戶端用來檢查或操作各種平台和架構的使用者介面。 使用者介面自動化可讓品質保證 (測試) 程式碼和協助工具應用程式 (例如螢幕助讀程式) 檢查使用者介面元素,並模擬從其他程式碼與它們進行的使用者互動。 如需跨所有平台之使用者介面自動化的資訊,請參閱<協助工具>。

本主題說明如何針對在 WPF 應用程式中執行的自訂控制項,實作伺服器端 UI 自動化提供者。 WPF 是透過與使用者介面元素樹狀結構平行的對等自動化物件樹狀結構,來支援使用者介面自動化。 提供協助工具功能的測試程式碼和應用程式,可以直接使用自動化對等物件 (針對同處理序程式碼),或透過由使用者介面自動化提供的通用介面來使用這些物件。

自動化對等類別

WPF 控制項是透過衍生自 AutomationPeer 的對等類別樹狀結構,來支援使用者介面自動化。 依照慣例,對等類別名稱會以控制項類別名稱開始,並以 "AutomationPeer" 結束。 例如,ButtonAutomationPeerButton 控制項類別的對等類別。 對等類別大致上相當於使用者介面自動化控制項類型,但專屬於 WPF 元素。 透過使用者介面自動化介面存取 WPF 應用程式的自動化程式碼並不會直接使用自動化對等,但相同處理序空間中的自動化程式碼則可以直接使用自動化對等。

內建自動化對等類別

如果元素接受來自使用者的介面活動,或包含螢幕助讀應用程式使用者所需的資訊,便會實作自動化對等類別。 並非所有 WPF 視覺元素都有自動化對等。 會實作自動化對等的類別範例為 ButtonTextBoxLabel。 不會實作自動化對等的類別範例,為衍生自 Decorator 的類別 (例如 Border),以及以 Panel 為基礎的類別 (例如 GridCanvas)。

基底 Control 類別沒有相對應的對等類別。 如果您希望對等類別和衍生自 Control 的自訂控制項相對應,您應該從 FrameworkElementAutomationPeer 衍生自訂對等類別。

衍生對等的安全性考量

自動化對等必須在部分信任的環境中執行。 UIAutomationClient 組件中的程式碼並沒有針對在部分信任的環境中執行進行設定,因此自動化對等程式碼不應該參考該組件。 您應該改為使用 UIAutomationTypes 組件中的類別。 例如,您應該使用來自 UIAutomationTypes 組件的 AutomationElementIdentifiers 類別,它與 UIAutomationClient 組件中的 AutomationElement 類別相對應。 在自動化對等程式碼中參考 UIAutomationTypes 組件是安全的。

對等瀏覽

在找到自動化對等之後,同處理序程式碼可以透過呼叫物件的 GetChildrenGetParent 方法來瀏覽對等樹狀結構。 瀏覽控制項內的 WPF 元素,是透過由對等實作 GetChildrenCore 方法來支援。 UI 自動化系統會呼叫此方法來建置控制項內所包含之子元素的樹狀結構,例如清單方塊中的清單項目。 預設的 UIElementAutomationPeer.GetChildrenCore 方法會周遊元素的視覺化樹狀結構,以建置自動化對等的樹狀結構。 自訂控制項會覆寫此方法,以將子系元素公開給自動化用戶端,並傳回能傳達資訊或允許使用者互動的元素自動化對等。

衍生對等中的自訂

所有衍生自 UIElementContentElement 的類別,都包含受保護的虛擬方法 OnCreateAutomationPeer。 WPF 會呼叫 OnCreateAutomationPeer 以取得每個控制項的自動化對等物件。 自動化程式碼可以使用對等來取得控制項特性和功能的相關資訊,並模擬互動式使用。 支援自動化的自訂控制項必須覆寫 OnCreateAutomationPeer,並傳回衍生自 AutomationPeer 之類別的執行個體。 例如,如果自訂控制項是衍生自 ButtonBase 類別,則由 OnCreateAutomationPeer 所傳回的物件應該會衍生自 ButtonBaseAutomationPeer

實作自訂控制項時,您必須從描述您自訂控制項唯一且特定之行為的基底自動化對等類別,覆寫 "Core" 方法。

覆寫 OnCreateAutomationPeer

覆寫您自訂控制項的 OnCreateAutomationPeer 方法,使它會傳回您的提供者物件。提供者物件必須直接或間接衍生自 AutomationPeer

覆寫 GetPattern

自動化對等可簡化伺服器端使用者介面自動化提供者的部分實作層面,但自訂控制項自動化對等仍須處理模式介面。 和非 WPF 提供者一樣,對等支援是透過提供 System.Windows.Automation.Provider 命名空間中介面 (例如 IInvokeProvider) 的實作來控制模式。 控制項模式介面可以由對等本身實作,或是由另一個物件實作。 對等的 GetPattern 實作會傳回支援指定模式的物件。 使用者介面自動化程式碼會呼叫 GetPattern 方法並指定 PatternInterface 列舉值。 您針對 GetPattern 的覆寫應該會傳回實作指定模式的物件。 如果您的控制項沒有模式的自訂實作,您可以呼叫基底類型的 GetPattern 實作,以擷取其實作或 Null (如果此控制項類型不支援該模式)。 例如,自訂的 NumericUpDown 控制項可以設定為某個範圍內的值,使其使用者介面自動化對等會實作 IRangeValueProvider 介面。 下列範例示範如何覆寫對等的 GetPattern 方法以回應 PatternInterface.RangeValue 值。

public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    return base.GetPattern(patternInterface);
}
Public Overrides Function GetPattern(ByVal patternInterface As PatternInterface) As Object
    If patternInterface = PatternInterface.RangeValue Then
        Return Me
    End If
    Return MyBase.GetPattern(patternInterface)
End Function

GetPattern 方法也可以指定子元素作為模式提供者。 下列程式碼示範 ItemsControl 如何將捲動模式處理傳送至其內部 ScrollViewer 控制項的對等。

public override object GetPattern(PatternInterface patternInterface)  
{  
    if (patternInterface == PatternInterface.Scroll)  
    {  
        ItemsControl owner = (ItemsControl) base.Owner;  
  
        // ScrollHost is internal to the ItemsControl class  
        if (owner.ScrollHost != null)  
        {  
            AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost);  
            if ((peer != null) && (peer is IScrollProvider))  
            {  
                peer.EventsSource = this;  
                return (IScrollProvider) peer;  
            }  
        }  
    }  
    return base.GetPattern(patternInterface);  
}  
Public Class Class1  
    Public Overrides Function GetPattern(ByVal patternInterface__1 As PatternInterface) As Object  
        If patternInterface1 = PatternInterface.Scroll Then  
            Dim owner As ItemsControl = DirectCast(MyBase.Owner, ItemsControl)  
  
            ' ScrollHost is internal to the ItemsControl class  
            If owner.ScrollHost IsNot Nothing Then  
                Dim peer As AutomationPeer = UIElementAutomationPeer.CreatePeerForElement(owner.ScrollHost)  
                If (peer IsNot Nothing) AndAlso (TypeOf peer Is IScrollProvider) Then  
                    peer.EventsSource = Me  
                    Return DirectCast(peer, IScrollProvider)  
                End If  
            End If  
        End If  
        Return MyBase.GetPattern(patternInterface1)  
    End Function  
End Class  

為了指定模式處理的子元素,此程式碼會取得子元素物件,使用 CreatePeerForElement 方法來建立對等,將新對等的 EventsSource 屬性設定為目前的對等,然後傳回新對等。 在子元素上設定 EventsSource 可防止子元素出現在自動化對等樹狀結構中,並將由子元素引發的所有事件,指定為源自於 EventsSource 中所指定的控制項。 ScrollViewer 控制項不會出現在自動化樹狀結構中,而它所產生的捲動事件會顯示為源自於 ItemsControl 物件。

覆寫 "Core" 方法

自動化程式碼會呼叫對等類別的公用方法,來取得控制項的相關資訊。 若要提供您控制項的相關資訊,請在您的控制項實作和由基底自動化對等類別所提供的不同時,覆寫所有名稱以 "Core" 為結尾的方法。 您的控制項至少必須實作 GetClassNameCoreGetAutomationControlTypeCore 方法,如下列範例所示。

protected override string GetClassNameCore()
{
    return "NumericUpDown";
}

protected override AutomationControlType GetAutomationControlTypeCore()
{
    return AutomationControlType.Spinner;
}
Protected Overrides Function GetClassNameCore() As String
    Return "NumericUpDown"
End Function

Protected Overrides Function GetAutomationControlTypeCore() As AutomationControlType
    Return AutomationControlType.Spinner
End Function

您的 GetAutomationControlTypeCore 實作會透過傳回 ControlType 值來描述您的控制項。 雖然您可以傳回 ControlType.Custom,您應該在可以更加精確地描述您控制項的前提下,傳回其中一個更加確切的控制項類型。 ControlType.Custom 的傳回值會使提供者需執行額外的工作以實作使用者介面自動化,且使用者介面自動化用戶端產品將無法預期控制項結構、鍵盤互動,以及可能的控制項模式。

實作 IsContentElementCoreIsControlElementCore 方法以指出您的控制項是否包含資料內容,或是能滿足使用者介面中的互動式角色 (或兩者皆是)。 根據預設,這兩個方法皆會傳回 true。 這些設定能改善自動化工具 (例如螢幕助讀程式) 的可用性,這些工具可以使用這些方法來篩選自動化樹狀結構。 如果您的 GetPattern 方法將模式處理傳送至子元素對等,則子元素對等的 IsControlElementCore 方法可能會傳回 False,以在自動化樹狀結構中隱藏該子元素對等。 例如,ListBox 中的捲動是由 ScrollViewer 負責處理,而 PatternInterface.Scroll 的自動化對等是由與 ListBoxAutomationPeer 相關聯之 ScrollViewerAutomationPeerGetPattern 方法傳回。因此,ScrollViewerAutomationPeerIsControlElementCore 方法會傳回 false,使 ScrollViewerAutomationPeer 不會出現在自動化樹狀結構中。

您的自動化對等應該為您的控制項提供適當的預設值。 請注意,參考您控制項的 XAML 可以透過包含 AutomationProperties 屬性來覆寫您核心方法的對等實作。 例如,下列 XAML 會建立有兩個自訂使用者介面自動化屬性的按鈕。

<Button AutomationProperties.Name="Special"
    AutomationProperties.HelpText="This is a special button."/>  

實作模式提供者

由自訂提供者實作的介面,會在擁有元素是直接衍生自 Control 的情況下明確宣告。 例如,下列程式碼會針對實作範圍值的 Control 宣告對等。

public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }  
Public Class RangePeer1  
    Inherits FrameworkElementAutomationPeer  
    Implements IRangeValueProvider  
End Class  

如果擁有控制項是衍生自特定的控制項類型 (例如 RangeBase),則對等將可以衍生自對應的衍生對等類別。 在此情況下,該對等將會衍生自 RangeBaseAutomationPeer (它能提供 IRangeValueProvider 的基底實作)。 下列程式碼顯示這種對等的宣告。

public class RangePeer2 : RangeBaseAutomationPeer { }  
Public Class RangePeer2  
    Inherits RangeBaseAutomationPeer  
End Class  

如需範例實作,請參閱實作和使用 NumericUpDown 自訂控制項的 C#Visual Basic 原始程式碼。

引發事件

自動化用戶端可以訂閱自動化事件。 自訂控制項必須透過呼叫 RaiseAutomationEvent 方法,以報告控制項狀態的變更。 同樣地,它會在屬性值發生變更的情況下呼叫 RaisePropertyChangedEvent 方法。 下列程式碼示範如何從控制項程式碼內取得對等物件,並呼叫方法以引發事件。 做為最佳化的手段,程式碼會判斷此事件類型是否有任何接聽程式。 只在有接聽程式的情況下引發事件,可以避免不必要的額外負荷並協助保持控制項的反應性。

if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
    NumericUpDownAutomationPeer peer =
        UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;

    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}
If AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged) Then
    Dim peer As NumericUpDownAutomationPeer = TryCast(UIElementAutomationPeer.FromElement(nudCtrl), NumericUpDownAutomationPeer)

    If peer IsNot Nothing Then
        peer.RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty, CDbl(oldValue), CDbl(newValue))
    End If
End If

另請參閱