服务器端 UI 自动化提供程序的实现

备注

本文档适用于希望使用 .NET Framework 中定义的托管类UI 自动化托管类的 System.Windows.Automation 开发人员。 有关应用程序的最新UI 自动化,请参阅 Windows 自动化API:UI 自动化。

本部分将介绍如何实现自定义控件的服务器端 UI 自动化提供程序。

WPF Windows Presentation Foundation () 元素和非 WPF 元素的实现 (如为 Windows 窗体) 设计的元素。 WPF 元素通过派生UI 自动化类提供对属性的支持 AutomationPeer 。 非 WPF 元素通过提供程序接口的实现提供支持。

安全注意事项

应编写提供程序,使它们能够在部分信任环境中的工作。 因为 UIAutomationClient.dll 未配置为在部分信任下运行,所以提供程序代码不应引用该程序集。 如果情况如此,则代码可以在完全信任环境中运行,但无法在部分信任环境中运行。

特别是,不要使用 UIAutomationClient.dll 中类的字段,例如 AutomationElement中的字段。 请改用 UIAutomationTypes.dll 中类的等效字段,如 AutomationElementIdentifiers

通过 Windows Presentation Foundation 元素实现的提供程序实现

有关本主题的详细信息,请参阅 WPF 自定义控件的 UI 自动化

通过非 WPF 元素实现的提供程序实现

自定义控件不是 WPF 框架的一部分,但以托管代码编写 (这些控件通常是 Windows Forms 控件) ,通过实现接口为 UI 自动化 提供支持。 每个元素必须实现至少一个下一部分中第一个表中列出的接口。 此外,如果该元素支持一个或多个控件模式,它必须实现每个控件模式的相应接口。

你的UI 自动化提供程序项目必须引用以下程序集:

  • UIAutomationProviders.dll

  • UIAutomationTypes.dll

  • WindowsBase.dll

提供程序接口

每个UI 自动化提供程序都必须实现以下接口之一。

接口 描述
IRawElementProviderSimple 为承载在窗口中的简单控件提供功能,包括支持控件模式和属性。
IRawElementProviderFragment 继承自 IRawElementProviderSimple。 为复杂控件中的元素添加功能,包括在片段中导航、设置焦点并返回该元素的边框。
IRawElementProviderFragmentRoot 继承自 IRawElementProviderFragment。 为复杂控件中的根元素添加功能,包括找到指定坐标处的子元素和设置整个控件的焦点状态。

以下接口可提供额外的功能,但并不需要实现。

接口 描述
IRawElementProviderAdviseEvents 启用提供程序跟踪事件请求。
IRawElementProviderHwndOverride 在片段的树中UI 自动化基于窗口的元素的重新定位。

System.Windows.Automation.Provider 命名空间中的所有其他接口用于控件模式支持。

非 WPF 提供程序的要求

为了与UI 自动化通信,控件必须实现以下主要功能区域:

功能 实现
向用户公开UI 自动化 为响应发送到控制窗口的 WM_GETOBJECT 消息,返回实现 IRawElementProviderSimple (或派生接口)的对象。 对于片段,这必须是片段根的提供程序。
提供属性值 实现 GetPropertyValue 以提供或重写值。
启用客户端从而与控件交互 实现支持控件模式的接口,如 IInvokeProvider。 在你的 GetPatternProvider实现中返回这些模式提供程序。
引发事件 调用 AutomationInteropProvider 的静态方法之一,以引发客户端可以侦听的一个事件。
启用导航并将在片段中设置焦点 为片段中的每个元素实现 IRawElementProviderFragment 。 (对于并非为片段的一部分的元素,此操作并不必要。)
启用设置焦点,以及查找片段中的子元素 实现 IRawElementProviderFragmentRoot。 (对于并非片段根的元素,此操作并不必要。)

非 WPF 提供程序中的属性值

UI 自动化控件的提供程序必须支持由自动化系统以及客户端应用程序使用的某些属性。 对于承载在 HWND (窗口) ,UI 自动化可以从默认窗口提供程序检索某些属性,但必须从自定义提供程序获取其他属性。

基于 HWND 控件的提供程序通常不需要提供以下属性(由字段值标识):

备注

承载在窗口的简单元素或片段根的 RuntimeIdProperty 是从窗口中获取的;但是,根下的片段元素(如列表框中的列表项)必须提供自己的标识符。 有关详细信息,请参阅 GetRuntimeId

IsKeyboardFocusableProperty应为托管在窗体控件中的提供程序Windows返回 。 在这种情况下,默认的窗口提供程序可能无法检索正确值。

NameProperty 通常由宿主提供程序提供。 例如,如果自定义控件从 Control派生,则名称从控件的 Text 属性派生。

有关示例代码,请参阅 Return Properties from a UI Automation Provider

非 WPF 提供程序中的事件

UI 自动化提供程序应引发事件,以通知客户端应用程序 UI 状态的更改。 以下方法用于引发事件。

方法 说明
RaiseAutomationEvent 引发各种事件,包括由控件模式触发的事件。
RaiseAutomationPropertyChangedEvent 当属性更改时UI 自动化引发事件。
RaiseStructureChangedEvent 当树的结构发生更改时UI 自动化事件;例如,通过删除或添加元素。

事件的目的是通知客户端用户界面 (UI) 中发生 (,无论活动是否由 UI 自动化 系统本身触发。 例如,由 InvokedEvent 标识的事件应当在每次调用该控件时引发,或通过直接用户输入或通过客户端应用程序调用 Invoke

若要优化性能,提供程序可以有选择地引发事件,或者,如果没有注册任何接收事件的客户端应用程序,则不引发任何事件。 以下方法用于进行优化。

方法 说明
ClientsAreListening 此静态属性指定任何客户端应用程序是否已订阅UI 自动化事件。
IRawElementProviderAdviseEvents 提供程序在片段根上对此接口的实现使其能够在当客户端在片段上注册和注销事件处理程序时接收到通知。

非 WPF 提供程序导航

简单控件的提供程序(如 HWND (窗口中托管的自定义) 无需支持在 UI 自动化 树中导航。 导航到元素和从元素导航由主机窗口的默认提供程序处理,此程序在 HostRawElementProvider的实现中指定。 但是,在实现复杂自定义控件的提供程序时,必须支持片段及其子代的根节点之间的导航,以及同级节点之间的导航。

备注

与根不同,片段的元素必须从 null 返回 HostRawElementProvider引用,因为它们没有直接承载在窗口中,并且没有默认的提供程序可以支持往返它们的导航。

片段的结构由你的 Navigate的实现决定。 对于自每个片段的每个可能的方向,此方法返回该方向的元素的提供程序对象。 如果该方向没有任何元素,则方法将返回 null 引用。

片段根仅支持子元素的导航。 例如,方向是 FirstChild时,列表框将返回列表中的第一项,当方向是 LastChild时,将返回最后一项。 片段根不支持导航到父级或同级;这由宿主窗口提供程序进行处理。

不是根的片段元素必须支持导航到父级、所有同级及其子级。

非 WPF 提供程序重新设置父级

弹出窗口实际是顶级窗口,因此默认情况下显示在 UI 自动化树中作为桌面的子项。 但是,在许多情况下,弹出式窗口在逻辑上是一些其他控件的子级。 例如,组合框的下拉列表在逻辑上是组合框的子级。 同样,菜单弹出窗口在逻辑上是菜单的子级。 UI 自动化支持对弹出窗口进行重定父级,使其看起来像是关联控件的子级。

若要重新设置弹出窗口的父级:

  1. 为弹出窗口的创建一个提供程序。 这要求预先知道弹出窗口的类。

  2. 照常实现弹出窗口的所有属性和模式,就像它是独立的控件一样。

  3. 实现 HostRawElementProvider 属性,以便其返回从 HostProviderFromHandle获取的值,其中该参数是弹出窗口的窗口句柄。

  4. 实现弹出窗口和其父级的 Navigate ,以便正确处理从逻辑父级到逻辑子级以及同级子级之间的导航。

当 UI 自动化遇到弹出窗口时,它会识别出正在从默认值覆盖导航,并在遇到作为桌面子级的弹出窗口时跳过该窗口。 而节点将只能通过片段到达。

重新设置父级不适合控件可以承载任何类窗口的情况。 例如,rebar 在其带区中可承载任何类型的 HWND。 为了处理这些情况,UI 自动化支持另一种形式的 HWND 重定位,如下一节中所述。

非 WPF 提供程序重定位

UI 自动化片段可能包含两个或多个元素,每个元素都包含在 (HWND) 的窗口中。 由于每个 HWND 都有自己的默认提供程序,该提供程序将 HWND 视为包含 HWND 的子级,因此,默认情况下,UI 自动化树将在片段中将 Hwnd 显示为父窗口的子级。 在大多数情况下,这是理想的行为,但有时会导致混淆,因为它与 UI 的逻辑结构不匹配。

一个很好的示例则是 rebar 控件。 Rebar 控件包含带区,其中每个带区又可以包含如工具栏、编辑框或组合框等基于 HWND 的控件。 Rebar HWND 的默认窗口提供程序将带区控件 HWND 视为子级,而 rebar 提供程序将带区视为子级。 因为 HWND 提供程序和 rebar 提供程序是协同工作,并且合并其子级,所以带区和基于 HWND 的控件都将显示为 rebar 的子级。 但是,在逻辑上,只有带区应显示为 rebar 的子级,并且每个带区提供程序应与它所包含控件的默认 HWND 提供程序结合使用。

为此,rebar 的片段根提供程序公开表示带区的子级集。 每个带区包含可能会公开属性和模式的单个提供程序。 在其实现 HostRawElementProvider的过程中,带区提供程序将返回控件 HWND的默认窗口提供程序,它通过调用 HostProviderFromHandle获取此提供程序,并传入控件的窗口句柄。 最后,rebar 的片段根提供程序将实现 IRawElementProviderHwndOverride 接口,并在其实现 GetOverrideProviderForHwnd 的过程中返回包含在指定 HWND 中的控件的相应带区提供程序。

另请参阅