服务器端 UI 自动化提供程序的实现Server-Side UI Automation Provider Implementation

备注

本文档适用于想要使用 UI 自动化UI Automation 命名空间中定义的托管 System.Windows.Automation 类的 .NET Framework 开发人员。This documentation is intended for .NET Framework developers who want to use the managed UI 自动化UI Automation classes defined in the System.Windows.Automation namespace. 有关 UI 自动化UI Automation的最新信息,请参阅 Windows 自动化 API:UI 自动化For the latest information about UI 自动化UI Automation, see Windows Automation API: UI Automation.

本部分将介绍如何实现自定义控件的服务器端 UI 自动化提供程序。This section describes how to implement a server-side UI Automation provider for a custom control.

Windows Presentation Foundation (WPF) 元素和非 WPF Windows 窗体 (元素的实现在本质上是不同的。The implementation for Windows Presentation Foundation (WPF) elements and non-WPF elements (such as those designed for Windows Forms) is fundamentally different. WPF 元素通过从派生的类提供对的支持 UI 自动化UI Automation AutomationPeerWPF elements provide support for UI 自动化UI Automation through a class derived from AutomationPeer. 非 WPF 元素通过提供程序接口的实现提供支持。Non-WPF elements provide support through implementations of provider interfaces.

安全注意事项Security Considerations

应编写提供程序,使它们能够在部分信任环境中的工作。Providers should be written so that they can work in a partial-trust environment. 因为 UIAutomationClient.dll 未配置为在部分信任下运行,所以提供程序代码不应引用该程序集。Because UIAutomationClient.dll is not configured to run under partial trust, your provider code should not reference that assembly. 如果情况如此,则代码可以在完全信任环境中运行,但无法在部分信任环境中运行。If it does so, the code may run in a full-trust environment but then fail in a partial-trust environment.

特别是,不要使用 UIAutomationClient.dll 中类的字段,例如 AutomationElement中的字段。In particular, do not use fields from classes in UIAutomationClient.dll such as those in AutomationElement. 请改用 UIAutomationTypes.dll 中类的等效字段,如 AutomationElementIdentifiersInstead, use the equivalent fields from classes in UIAutomationTypes.dll, such as AutomationElementIdentifiers.

通过 Windows Presentation Foundation 元素实现的提供程序实现Provider Implementation by Windows Presentation Foundation Elements

有关本主题的详细信息,请参阅 WPF 自定义控件的 UI 自动化For more information on this topic, please see UI Automation of a WPF Custom Control.

通过非 WPF 元素实现的提供程序实现Provider Implementation by non-WPF Elements

不属于 WPF 框架但以托管代码编写的自定义控件 (最常见的是 Windows 窗体控件) , UI 自动化UI Automation 通过实现接口为提供支持。Custom controls that are not part of the WPF framework, but that are written in managed code (most often these are Windows Forms controls), provide support for UI 自动化UI Automation by implementing interfaces. 每个元素必须实现至少一个下一部分中第一个表中列出的接口。Every element must implement at least one of the interfaces listed in the first table in the next section. 此外,如果该元素支持一个或多个控件模式,它必须实现每个控件模式的相应接口。In addition, if the element supports one or more control patterns, it must implement the appropriate interface for each control pattern.

你的 UI 自动化UI Automation 提供程序项目必须引用以下程序集:Your UI 自动化UI Automation provider project must reference the following assemblies:

  • UIAutomationProviders.dllUIAutomationProviders.dll

  • UIAutomationTypes.dllUIAutomationTypes.dll

  • WindowsBase.dllWindowsBase.dll

提供程序接口Provider Interfaces

每个 UI 自动化UI Automation 提供程序必须实现以下的一个接口。Every UI 自动化UI Automation provider must implement one of the following interfaces.

接口Interface 说明Description
IRawElementProviderSimple 为承载在窗口中的简单控件提供功能,包括支持控件模式和属性。Provides functionality for a simple control hosted in a window, including support for control patterns and properties.
IRawElementProviderFragment 继承自 IRawElementProviderSimpleInherits from IRawElementProviderSimple. 为复杂控件中的元素添加功能,包括在片段中导航、设置焦点并返回该元素的边框。Adds functionality for an element in a complex control, including navigation within the fragment, setting focus, and returning the bounding rectangle of the element.
IRawElementProviderFragmentRoot 继承自 IRawElementProviderFragmentInherits from IRawElementProviderFragment. 为复杂控件中的根元素添加功能,包括找到指定坐标处的子元素和设置整个控件的焦点状态。Adds functionality for the root element in a complex control, including locating a child element at specified coordinates and setting the focus state for the entire control.

以下接口可提供额外的功能,但并不需要实现。The following interfaces provide added functionality but are not required to be implemented.

接口Interface 说明Description
IRawElementProviderAdviseEvents 启用提供程序跟踪事件请求。Enables the provider to track requests for events.
IRawElementProviderHwndOverride 启用对片段的 UI 自动化UI Automation 树内的基于窗口的元素的重定位。Enables repositioning of window-based elements within the UI 自动化UI Automation tree of a fragment.

System.Windows.Automation.Provider 命名空间中的所有其他接口用于控件模式支持。All other interfaces in the System.Windows.Automation.Provider namespace are for control pattern support.

非 WPF 提供程序的要求Requirements for Non-WPF Providers

为了与 UI 自动化UI Automation进行通信,你的控件必须实现以下主要领域的功能:In order to communicate with UI 自动化UI Automation, your control must implement the following main areas of functionality:

功能Functionality 实现Implementation
UI 自动化UI Automation公开提供程序Expose the provider to UI 自动化UI Automation 为响应发送到控制窗口的 WM_GETOBJECT 消息,返回实现 IRawElementProviderSimple (或派生接口)的对象。In response to a WM_GETOBJECT message sent to the control window, return the object that implements IRawElementProviderSimple (or a derived interface). 对于片段,这必须是片段根的提供程序。For fragments, this must be the provider for the fragment root.
提供属性值Provide property values 实现 GetPropertyValue 以提供或重写值。Implement GetPropertyValue to provide or override values.
启用客户端从而与控件交互Enable the client to interact with the control 实现支持控件模式的接口,如 IInvokeProviderImplement interfaces that support control patterns, such as IInvokeProvider. 在你的 GetPatternProvider实现中返回这些模式提供程序。Return these pattern providers in your implementation of GetPatternProvider.
引发事件Raise events 调用 AutomationInteropProvider 的静态方法之一,以引发客户端可以侦听的一个事件。Call one of the static methods of AutomationInteropProvider to raise an event that a client can listen for.
启用导航并将在片段中设置焦点Enable navigation and focusing within a fragment 为片段中的每个元素实现 IRawElementProviderFragmentImplement IRawElementProviderFragment for each element within the fragment. (对于并非为片段的一部分的元素,此操作并不必要。)(Not necessary for elements that are not part of a fragment.)
启用设置焦点,以及查找片段中的子元素Enable focusing and location of child element in a fragment 实现 IRawElementProviderFragmentRootImplement IRawElementProviderFragmentRoot. (对于并非片段根的元素,此操作并不必要。)(Not necessary for elements that are not fragment roots.)

非 WPF 提供程序中的属性值Property Values in Non-WPF Providers

自定义控件的UI 自动化UI Automation 提供程序必须支持特定属性,这些属性可由自动化系统以及客户端应用程序使用。UI 自动化UI Automation providers for custom controls must support certain properties that can be used by the automation system as well as by client applications. 对于窗口 (HWND) 中承载的元素, UI 自动化UI Automation 可以从默认窗口提供程序检索某些属性,但必须从自定义提供程序获取其他属性。For elements that are hosted in windows (HWNDs), UI 自动化UI Automation can retrieve some properties from the default window provider, but must obtain others from the custom provider.

基于 HWND 控件的提供程序通常不需要提供以下属性(由字段值标识):Providers for HWND based controls do not usually need to provide the following properties (identified by field values):

备注

承载在窗口的简单元素或片段根的 RuntimeIdProperty 是从窗口中获取的;但是,根下的片段元素(如列表框中的列表项)必须提供自己的标识符。The RuntimeIdProperty of a simple element or fragment root hosted in a window is obtained from the window; however, fragment elements below the root (such as list items in a list box) must provide their own identifiers. 有关详细信息,请参阅 GetRuntimeIdFor more information, see GetRuntimeId.

IsKeyboardFocusableProperty应为 Windows 窗体控件中承载的提供程序返回。The IsKeyboardFocusableProperty should be returned for providers hosted in a Windows Forms control. 在这种情况下,默认的窗口提供程序可能无法检索正确值。In this case, the default window provider may be unable to retrieve the correct value.

NameProperty 通常由宿主提供程序提供。The NameProperty is usually supplied by the host provider. 例如,如果自定义控件从 Control派生,则名称从控件的 Text 属性派生。For example, if a custom control is derived from Control, the name is derived from the Text property of the control.

有关示例代码,请参阅 Return Properties from a UI Automation ProviderFor example code, see Return Properties from a UI Automation Provider.

非 WPF 提供程序中的事件Events in Non-WPF Providers

UI 自动化UI Automation 提供程序应引发事件以通知客户端应用程序有关 UI 状态的变化。providers should raise events to notify client applications of changes in the state of the UI. 以下方法用于引发事件。The following methods are used to raise events.

方法Method 说明Description
RaiseAutomationEvent 引发各种事件,包括由控件模式触发的事件。Raises various events, including events triggered by control patterns.
RaiseAutomationPropertyChangedEvent UI 自动化UI Automation 属性更改时引发事件。Raises an event when a UI 自动化UI Automation property has changed.
RaiseStructureChangedEvent UI 自动化UI Automation 树的结构更改时引发事件;例如,通过删除或添加一个元素。Raises an event when the structure of the UI 自动化UI Automation tree has changed; for example, by the removal or addition of an element.

事件的目的是通知客户端 用户界面 (UI)user interface (UI)中发生的情况,该活动是否由 UI 自动化UI Automation 系统本身触发。The purpose of an event is to notify the client of something taking place in the 用户界面 (UI)user interface (UI), whether or not the activity is triggered by the UI 自动化UI Automation system itself. 例如,由 InvokedEvent 标识的事件应当在每次调用该控件时引发,或通过直接用户输入或通过客户端应用程序调用 InvokeFor example, the event identified by InvokedEvent should be raised whenever the control is invoked, either through direct user input or by the client application calling Invoke.

若要优化性能,提供程序可以有选择地引发事件,或者,如果没有注册任何接收事件的客户端应用程序,则不引发任何事件。To optimize performance, a provider can selectively raise events, or raise no events at all if no client application is registered to receive them. 以下方法用于进行优化。The following methods are used for optimization.

方法Method 说明Description
ClientsAreListening 此静态属性指定是否存在已订阅 UI 自动化UI Automation 事件的客户端应用程序。This static property specifies whether any client applications have subscribed to UI 自动化UI Automation events.
IRawElementProviderAdviseEvents 提供程序在片段根上对此接口的实现使其能够在当客户端在片段上注册和注销事件处理程序时接收到通知。The provider's implementation of this interface on a fragment root enables it to be advised when clients register and unregister event handlers for events on the fragment.

非 WPF 提供程序导航Non-WPF Provider Navigation

简单控件的提供程序,例如窗口 (HWND) 中承载的自定义按钮,无需支持 UI 自动化UI Automation 树中的导航。Providers for simple controls such as a custom button hosted in a window (HWND) do not need to support navigation within the UI 自动化UI Automation tree. 导航到元素和从元素导航由主机窗口的默认提供程序处理,此程序在 HostRawElementProvider的实现中指定。Navigation to and from the element is handled by the default provider for the host window, which is specified in the implementation of HostRawElementProvider. 但是,在实现复杂自定义控件的提供程序时,必须支持片段及其子代的根节点之间的导航,以及同级节点之间的导航。When you implement a provider for a complex custom control, however, you must support navigation between the root node of the fragment and its descendants, and between sibling nodes.

备注

与根不同,片段的元素必须从 null 返回 HostRawElementProvider引用,因为它们没有直接承载在窗口中,并且没有默认的提供程序可以支持往返它们的导航。Elements of a fragment other than the root must return a null reference from HostRawElementProvider, because they are not directly hosted in a window, and no default provider can support navigation to and from them.

片段的结构由你的 Navigate的实现决定。The structure of the fragment is determined by your implementation of Navigate. 对于自每个片段的每个可能的方向,此方法返回该方向的元素的提供程序对象。For each possible direction from each fragment, this method returns the provider object for the element in that direction. 如果该方向没有任何元素,则方法将返回 null 引用。If there is no element in that direction, the method returns a null reference.

片段根仅支持子元素的导航。The fragment root supports navigation only to child elements. 例如,方向是 FirstChild时,列表框将返回列表中的第一项,当方向是 LastChild时,将返回最后一项。For example, a list box returns the first item in the list when the direction is FirstChild, and the last item when the direction is LastChild. 片段根不支持导航到父级或同级;这由宿主窗口提供程序进行处理。The fragment root does not support navigation to a parent or siblings; this is handled by the host window provider.

不是根的片段元素必须支持导航到父级、所有同级及其子级。Elements of a fragment that are not the root must support navigation to the parent, and to any siblings and children they have.

非 WPF 提供程序重新设置父级Non-WPF Provider Reparenting

弹出窗口实际是顶级窗口,所以默认情况下会作为桌面的子级显示在 UI 自动化UI Automation 树中。Pop-up windows are actually top-level windows, and so by default appear in the UI 自动化UI Automation tree as children of the desktop. 但是,在许多情况下,弹出式窗口在逻辑上是一些其他控件的子级。In many cases, however, pop-up windows are logically children of some other control. 例如,组合框的下拉列表在逻辑上是组合框的子级。For example, the drop-down list of a combo box is logically a child of the combo box. 同样,菜单弹出窗口在逻辑上是菜单的子级。Similarly, a menu pop-up window is logically a child of the menu. UI 自动化UI Automation 支持重新设置弹出窗口的父级,以便它们显示为关联控件的子级。provides support to reparent pop-up windows so that they appear to be children of the associated control.

若要重新设置弹出窗口的父级:To reparent a pop-up window:

  1. 为弹出窗口的创建一个提供程序。Create a provider for the pop-up window. 这要求预先知道弹出窗口的类。This requires that the class of the pop-up window is known in advance.

  2. 照常实现弹出窗口的所有属性和模式,就像它是独立的控件一样。Implement all properties and patterns as usual for that pop-up, as though it were a control in its own right.

  3. 实现 HostRawElementProvider 属性,以便其返回从 HostProviderFromHandle获取的值,其中该参数是弹出窗口的窗口句柄。Implement the HostRawElementProvider property so that it returns the value obtained from HostProviderFromHandle, where the parameter is the window handle of the pop-up window.

  4. 实现弹出窗口和其父级的 Navigate ,以便正确处理从逻辑父级到逻辑子级以及同级子级之间的导航。Implement Navigate for the pop-up window and its parent so that navigation is handled properly from the logical parent to the logical children, and between sibling children.

UI 自动化UI Automation 遇到弹出窗口,它可以识别导航正从默认进行重写,并会跳过作为桌面子级出现的弹出窗口。When UI 自动化UI Automation encounters the pop-up window, it recognizes that navigation is being overridden from the default, and skips over the pop-up window when it is encountered as a child of the desktop. 而节点将只能通过片段到达。Instead, the node will only be reachable through the fragment.

重新设置父级不适合控件可以承载任何类窗口的情况。Reparenting is not suitable for cases where a control can host a window of any class. 例如,rebar 在其带区中可承载任何类型的 HWND。For example, a rebar can host any type of HWND in its bands. 为了处理这些情况, UI 自动化UI Automation 支持 HWND 重定位的替代形式,如下节中所述。To handle these cases, UI 自动化UI Automation supports an alternative form of HWND relocation, as described in the next section.

非 WPF 提供程序重定位Non-WPF Provider Repositioning

UI 自动化UI Automation 片段可能包含两个或多个元素,其中每个元素都包含在一个窗口 (HWND) 中。fragments may contain two or more elements that are each contained in a window (HWND). 因为每个 HWND 都有自己的默认提供程序,该提供程序将 HWND 视为作为容器的 HWND 的子级,默认情况下, UI 自动化UI Automation 树将在片段中将 HWND 显示为父窗口的子级。Because each HWND has its own default provider that considers the HWND to be a child of a containing HWND, the UI 自动化UI Automation tree will, by default, show the HWNDs in the fragment as children of the parent window. 在大多数情况下,这是所需的行为,但有时这可能会导致混淆,因为它不匹配 UIUI的逻辑结构。In most cases this is desirable behavior, but sometimes it can lead to confusion because it does not match the logical structure of the UIUI.

一个很好的示例则是 rebar 控件。A good example of this is a rebar control. Rebar 控件包含带区,其中每个带区又可以包含如工具栏、编辑框或组合框等基于 HWND 的控件。A rebar contains bands, each of which can in turn contain an HWND-based control such as a toolbar, an edit box, or a combo box. Rebar HWND 的默认窗口提供程序将带区控件 HWND 视为子级,而 rebar 提供程序将带区视为子级。The default window provider for the rebar HWND sees the band control HWNDs as children, and the rebar provider sees the bands as children. 因为 HWND 提供程序和 rebar 提供程序是协同工作,并且合并其子级,所以带区和基于 HWND 的控件都将显示为 rebar 的子级。Because the HWND provider and the rebar provider are working in tandem and combining their children, both the bands and the HWND-based controls appear as children of the rebar. 但是,在逻辑上,只有带区应显示为 rebar 的子级,并且每个带区提供程序应与它所包含控件的默认 HWND 提供程序结合使用。Logically, however, only the bands should appear as children of the rebar, and each band provider should be coupled with the default HWND provider for the control it contains.

为此,rebar 的片段根提供程序公开表示带区的子级集。To accomplish this, the fragment root provider for the rebar exposes a set of children representing the bands. 每个带区包含可能会公开属性和模式的单个提供程序。Each band has a single provider that may expose properties and patterns. 在其实现 HostRawElementProvider的过程中,带区提供程序将返回控件 HWND的默认窗口提供程序,它通过调用 HostProviderFromHandle获取此提供程序,并传入控件的窗口句柄。In its implementation of HostRawElementProvider, the band provider returns the default window provider for the control HWND, which it obtains by calling HostProviderFromHandle, passing in the control's window handle. 最后,rebar 的片段根提供程序将实现 IRawElementProviderHwndOverride 接口,并在其实现 GetOverrideProviderForHwnd 的过程中返回包含在指定 HWND 中的控件的相应带区提供程序。Finally, the fragment root provider for the rebar implements the IRawElementProviderHwndOverride interface, and in its implementation of GetOverrideProviderForHwnd it returns the appropriate band provider for the control contained in the specified HWND.

请参阅See also