WPF 自定义控件的 UI 自动化UI Automation of a WPF Custom Control

Microsoft UI 自动化Microsoft UI Automation 提供了一个通用接口,自动化客户端可使用该接口来检查或操作各种平台和框架的用户界面。provides a single, generalized interface that automation clients can use to examine or operate the user interfaces of a variety of platforms and frameworks. UI 自动化UI Automation 使质量保证(测试)代码和具有辅助功能的应用程序(如屏幕阅读器)能够检查用户界面元素,以及能够模拟与其他代码中的用户元素进行的用户交互。enables both quality-assurance (test) code and accessibility applications such as screen readers to examine user-interface elements and simulate user interaction with them from other code. 有关跨所有平台的 UI 自动化UI Automation 的信息,请参阅“辅助功能”。For information about UI 自动化UI Automation across all platforms, see Accessibility.

本主题介绍如何实现在 WPF 应用程序中运行的自定义控件的服务器端 UI 自动化提供程序。This topic describes how to implement a server-side UI Automation provider for a custom control that runs in a WPF application. WPF 通过等同于用户界面元素树的对等自动化对象树来支持 UI 自动化UI AutomationWPF supports UI 自动化UI Automation through a tree of peer automation objects that parallels the tree of user interface elements. 测试代码和提供辅助功能的应用程序可以直接使用自动化对等对象(适用于进程内代码),也可以通过 UI 自动化UI Automation 提供的通用接口使用自动化对等对象。Test code and applications that provide accessibility features can use automation peer objects directly (for in-process code) or through the generalized interface provided by UI 自动化UI Automation.

自动化对等类Automation Peer Classes

WPF 控件支持 UI 自动化UI Automation 通过派生自的对等类树进行支持 AutomationPeerWPF controls support UI 自动化UI Automation through a tree of peer classes that derive from AutomationPeer. 按照约定,对等类名以控件类名开头,以“AutomationPeer”结尾。By convention, peer class names begin with the control class name and end with "AutomationPeer". 例如, ButtonAutomationPeer 是控件类的对等类 ButtonFor example, ButtonAutomationPeer is the peer class for the Button control class. 对等类大致等效于 UI 自动化UI Automation 控件类型,但特定于 WPF 元素。The peer classes are roughly equivalent to UI 自动化UI Automation control types but are specific to WPF elements. 通过 UI 自动化UI Automation 接口访问 WPF 应用程序的自动化代码不直接使用自动化对等,但同一进程空间中的自动化代码可以直接使用自动化对等。Automation code that accesses WPF applications through the UI 自动化UI Automation interface does not use automation peers directly, but automation code in the same process space can use automation peers directly.

内置的自动化对等类Built-in Automation Peer Classes

如果元素接受用户的接口活动,或者如果元素包含屏幕阅读器应用程序的用户所需的信息,则这些元素会实现一个自动化对等类。Elements implement an automation peer class if they accept interface activity from the user, or if they contain information needed by users of screen-reader applications. 并非所有 WPF 视觉元素都具有自动化对等。Not all WPF visual elements have automation peers. 实现自动化对等类的类的示例包括 ButtonTextBoxLabelExamples of classes that implement automation peers are Button, TextBox, and Label. 不实现自动化对等类的类的示例是派生自的类 Decorator ,如和基于的类,如 Border Panel GridCanvasExamples of classes that do not implement automation peers are classes that derive from Decorator, such as Border, and classes based on Panel, such as Grid and Canvas.

基类没有 Control 对应的对等类。The base Control class does not have a corresponding peer class. 如果需要对等类对应于从派生的自定义控件 Control ,则应从派生自定义对等类 FrameworkElementAutomationPeerIf you need a peer class to correspond to a custom control that derives from Control, you should derive the custom peer class from FrameworkElementAutomationPeer.

派生对等的安全注意事项Security Considerations for Derived Peers

自动化对等必须在部分信任环境中运行。Automation peers must run in a partial-trust environment. UIAutomationClient 程序集中的代码未配置为在部分信任环境中运行,自动化对等代码不应引用该程序集。Code in the UIAutomationClient assembly is not configured to run in a partial-trust environment, and automation peer code should not reference that assembly. 应改用 UIAutomationTypes 程序集中的类。Instead, you should use the classes in the UIAutomationTypes assembly. 例如,你应该使用 AutomationElementIdentifiers uiautomationtypes.dll 程序集中的类,该程序集对应于 AutomationElement uiautomationclient.dll 程序集中的类。For example, you should use the AutomationElementIdentifiers class from the UIAutomationTypes assembly, which corresponds to the AutomationElement class in the UIAutomationClient assembly. 可以安全地在自动化对等代码中引用 UIAutomationTypes 程序集。It is safe to reference the UIAutomationTypes assembly in automation peer code.

对等导航Peer Navigation

定位自动化对等节点后,进程内代码可以通过调用对象的和方法来导航对等树 GetChildren GetParentAfter locating an automation peer, in-process code can navigate the peer tree by calling the object's GetChildren and GetParent methods. 方法的对等实现支持在控件内的 WPF 元素间导航 GetChildrenCoreNavigation among WPF elements within a control is supported by the peer's implementation of the GetChildrenCore method. UI 自动化系统调用此方法生成控件内包含的子元素树(例如,列表框中的列表项)。The UI Automation system calls this method to build up a tree of subelements contained within a control; for example, list items in a list box. 默认 UIElementAutomationPeer.GetChildrenCore 方法遍历元素的可视化树,以生成自动化对等节点的树。The default UIElementAutomationPeer.GetChildrenCore method traverses the visual tree of elements to build the tree of automation peers. 自定义控件重写此方法以向自动化客户端公开子元素,这会返回传达信息或允许用户交互的元素自动化对等。Custom controls override this method to expose children elements to automation clients, returning the automation peers of elements that convey information or allow user interaction.

派生对等中的自定义项Customizations in a Derived Peer

派生自和的所有类都 UIElement ContentElement 包含受保护的虚拟方法 OnCreateAutomationPeerAll classes that derive from UIElement and ContentElement contain the protected virtual method OnCreateAutomationPeer. WPF 调用 OnCreateAutomationPeer 来获取每个控件的自动化对等对象。WPF calls OnCreateAutomationPeer to get the automation peer object for each control. 自动化代码可使用对等来获取有关控件特征和功能的信息,并模拟交互使用。Automation code can use the peer to get information about a control’s characteristics and features and to simulate interactive use. 支持自动化的自定义控件必须重写 OnCreateAutomationPeer 并返回派生自的类的实例 AutomationPeerA custom control that supports automation must override OnCreateAutomationPeer and return an instance of a class that derives from AutomationPeer. 例如,如果自定义控件派生自 ButtonBase 类,则返回的对象 OnCreateAutomationPeer 应派生自 ButtonBaseAutomationPeerFor example, if a custom control derives from the ButtonBase class, then the object returned by OnCreateAutomationPeer should derive from ButtonBaseAutomationPeer.

实现自定义控件时,必须重写自动化对等基类中的“Core”方法,这些方法描述了特定于自定义控件的唯一行为。When implementing a custom control, you must override the "Core" methods from the base automation peer class that describe behavior unique and specific to your custom control.

重写 OnCreateAutomationPeerOverride OnCreateAutomationPeer

OnCreateAutomationPeer为自定义控件重写方法,使其返回必须直接或间接从派生的提供程序对象 AutomationPeerOverride the OnCreateAutomationPeer method for your custom control so that it returns your provider object, which must derive directly or indirectly from AutomationPeer.

重写 GetPatternOverride GetPattern

自动化对等简化了服务器端 UI 自动化UI Automation 提供程序的一部分实现,但自定义控件自动化对等仍必须处理模式接口。Automation peers simplify some implementation aspects of server-side UI 自动化UI Automation providers, but custom control automation peers must still handle pattern interfaces. 与非 WPF 提供程序一样,对等方通过提供命名空间中的接口实现来支持控件模式 System.Windows.Automation.Provider ,如 IInvokeProviderLike non-WPF providers, peers support control patterns by providing implementations of interfaces in the System.Windows.Automation.Provider namespace, such as IInvokeProvider. 控件模式接口可以由对等本身或其他对象实现。The control pattern interfaces can be implemented by the peer itself or by another object. 的对等的实现 GetPattern 返回支持指定模式的对象。The peer's implementation of GetPattern returns the object that supports the specified pattern. UI 自动化UI Automation 代码调用 GetPattern 方法并指定 PatternInterface 枚举值。code calls the GetPattern method and specifies a PatternInterface enumeration value. 的重写 GetPattern 应返回实现指定模式的对象。Your override of GetPattern should return the object that implements the specified pattern. 如果控件没有模式的自定义实现,则可以调用的基类型的实现 GetPattern 来检索其实现,如果此控件类型不支持模式,则为 null。If your control does not have a custom implementation of a pattern, you can call the base type's implementation of GetPattern to retrieve either its implementation or null if the pattern is not supported for this control type. 例如,可以将自定义 NumericUpDown 控件设置为一个范围内的值,因此,其 UI 自动化UI Automation 对等端将实现 IRangeValueProvider 接口。For example, a custom NumericUpDown control can be set to a value within a range, so its UI 自动化UI Automation peer would implement the IRangeValueProvider interface. 下面的示例演示如何重写对等的 GetPattern 方法以响应某个 PatternInterface.RangeValue 值。The following example shows how the peer's GetPattern method is overridden to respond to a PatternInterface.RangeValue value.

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方法还可以将子元素指定为模式提供程序。A GetPattern method can also specify a subelement as a pattern provider. 下面的代码演示如何将 ItemsControl 滚动模式处理传输到其内部控件的对等方 ScrollViewerThe following code shows how ItemsControl transfers scroll pattern handling to the peer of its internal ScrollViewer control.

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 新对等方的属性设置为当前对等方,并返回新的对等。To specify a subelement for pattern handling, this code gets the subelement object, creates a peer by using the CreatePeerForElement method, sets the EventsSource property of the new peer to the current peer, and returns the new peer. EventsSource子元素上的设置可防止子元素出现在自动化对等树中,并将子元素引发的所有事件指定为源自中指定的控件 EventsSourceSetting EventsSource on a subelement prevents the subelement from appearing in the automation peer tree and designates all events raised by the subelement as originating from the control specified in EventsSource. ScrollViewer控件不会出现在自动化树中,并且该控件生成的滚动事件会显示为源自 ItemsControl 对象。The ScrollViewer control does not appear in the automation tree, and scrolling events that it generates appear to originate from the ItemsControl object.

重写“Core”方法Override "Core" Methods

自动化代码通过调用对等类的公共方法来获取控件的相关信息。Automation code gets information about your control by calling public methods of the peer class. 在控件实现不同于自动化对等基类提供的实现的情况下,若要提供控件的相关信息,请重写名称以“Core”结尾的每个方法。To provide information about your control, override each method whose name ends with "Core" when your control implementation differs from that of that provided by the base automation peer class. 控件至少必须实现 GetClassNameCoreGetAutomationControlTypeCore 方法,如以下示例中所示。At a minimum, your control must implement the GetClassNameCore and GetAutomationControlTypeCore methods, as shown in the following example.

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 通过返回值来描述控件 ControlTypeYour implementation of GetAutomationControlTypeCore describes your control by returning a ControlType value. 尽管可以返回 ControlType.Custom ,但如果它准确描述了控件,则应返回更具体的控件类型之一。Although you can return ControlType.Custom, you should return one of the more specific control types if it accurately describes your control. 返回值 ControlType.Custom 需要提供程序的额外工作才能实现 UI 自动化UI AutomationUI 自动化UI Automation 客户端产品无法预测控制结构、键盘交互和可能的控件模式。A return value of ControlType.Custom requires extra work for the provider to implement UI 自动化UI Automation, and UI 自动化UI Automation client products are unable to anticipate the control structure, keyboard interaction, and possible control patterns.

实现 IsContentElementCoreIsControlElementCore 方法,以指示控件是包含数据内容还是在用户界面中实现交互角色 (或同时) 。Implement the IsContentElementCore and IsControlElementCore methods to indicate whether your control contains data content or fulfills an interactive role in the user interface (or both). 默认情况下,这两个方法返回 trueBy default, both methods return true. 这些设置提高了屏幕阅读器等自动化工具的可用性,此类工具可使用这些方法筛选自动化树。These settings improve the usability of automation tools such as screen readers, which may use these methods to filter the automation tree. 如果 GetPattern 方法将模式处理传输到子元素对等方,则子元素对等的 IsControlElementCore 方法可以返回 false,以便在自动化树中隐藏子元素对等方。If your GetPattern method transfers pattern handling to a subelement peer, the subelement peer's IsControlElementCore method can return false to hide the subelement peer from the automation tree. 例如,中的滚动由 ListBox 处理 ScrollViewer ,并 PatternInterface.ScrollGetPattern 与关联的的方法返回的自动化对等方 ScrollViewerAutomationPeer ListBoxAutomationPeer 。因此,的 IsControlElementCore 方法 ScrollViewerAutomationPeer 返回 false ,因此不 ScrollViewerAutomationPeer 会出现在自动化树中。For example, scrolling in a ListBox is handled by a ScrollViewer, and the automation peer for PatternInterface.Scroll is returned by the GetPattern method of the ScrollViewerAutomationPeer that is associated with the ListBoxAutomationPeer.Therefore, the IsControlElementCore method of the ScrollViewerAutomationPeer returns false, so that the ScrollViewerAutomationPeer does not appear in the automation tree.

自动化对等应为控件提供合适的默认值。Your automation peer should provide appropriate default values for your control. 请注意,引用控件的 XAML 可以通过包含特性来重写核心方法的对等实现 AutomationPropertiesNote that XAML that references your control can override your peer implementations of core methods by including AutomationProperties attributes. 例如,以下 XAML 创建的按钮具有两个自定义的 UI 自动化UI Automation 属性。For example, the following XAML creates a button that has two customized UI 自动化UI Automation properties.

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

实现模式提供程序Implement Pattern Providers

如果拥有的元素直接从派生,则由自定义提供程序实现的接口是显式声明的 ControlThe interfaces implemented by a custom provider are explicitly declared if the owning element derives directly from Control. 例如,以下代码声明了一个实现范围值的对等方 ControlFor example, the following code declares a peer for a Control that implements a range value.

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

如果所属控件派生自特定类型的控件(如 RangeBase ),则对等方可以从等效的派生对等类派生。If the owning control derives from a specific type of control such as RangeBase, the peer can be derived from an equivalent derived peer class. 在这种情况下,对等方将派生自 RangeBaseAutomationPeer ,后者提供的基实现 IRangeValueProviderIn this case, the peer would derive from RangeBaseAutomationPeer, which supplies a base implementation of IRangeValueProvider. 以下代码演示了此类对等的声明。The following code shows the declaration of such a peer.

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

有关示例实现,请参阅 c # 或实现和使用 NumericUpDown 自定义控件的 Visual Basic 源代码。For an example implementation, see the C# or Visual Basic source code that implements and consumes a NumericUpDown custom control.

引发事件Raise Events

自动化客户端可订阅自动化事件。Automation clients can subscribe to automation events. 自定义控件必须通过调用方法来报告对控件状态的更改 RaiseAutomationEventCustom controls must report changes to control state by calling the RaiseAutomationEvent method. 同样,在属性值更改时,调用 RaisePropertyChangedEvent 方法。Similarly, when a property value changes, call the RaisePropertyChangedEvent method. 以下代码演示了如何在控件代码内获取对等对象,并调用一个方法来引发事件。The following code shows how to get the peer object from within the control code and call a method to raise an event. 作为一种优化,该代码会确定是否有适用于此事件类型的任何侦听器。As an optimization, the code determines if there are any listeners for this event type. 仅当有侦听器时才引发事件,可避免不必要的开销,有助于控件保持响应状态。Raising the event only when there are listeners avoids unnecessary overhead and helps the control remain responsive.

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

另请参阅See also