WPF 自定义控件的 UI 自动化

更新: 2008 年 7 月

Microsoft UI 自动化提供了一个通用接口,自动化客户端可以使用此接口来检查或操作各种平台和框架的用户界面。借助 UI 自动化,质量保证(测试)代码和辅助功能应用程序(如屏幕阅读器)都可以检查用户界面元素并可以从其他代码模拟用户与这些元素的交互。有关所有平台上 UI 自动化的信息,请参见可访问性

本主题介绍如何为在 WPF 应用程序中运行的自定义控件实现服务器端 UI 自动化提供程序。WPF 通过与用户界面元素树等同的对等类自动化对象树来支持 UI 自动化。提供辅助功能的测试代码和应用程序可以直接使用自动化对等类对象(对于进程内代码),也可以通过 UI 自动化提供的通用接口使用。

本主题包括下列各节。

  • 自动化对等类
  • 内置的自动化对等类
  • 派生对等类的安全性注意事项
  • 对等类导航
  • 派生对等类中的自定义项
  • 相关主题

自动化对等类

WPF 控件通过派生自 AutomationPeer 的对等类的树来支持 UI 自动化。按照约定,对等类的名称须以控件类的名称开头,并以“AutomationPeer”结尾。例如,ButtonAutomationPeerButton 控件类的对等类。对等类大致上等同于 UI 自动化控件类型,只不过对等类是特定于 WPF 元素的。通过 UI 自动化接口访问 WPF 应用程序的自动化代码不直接使用自动化对等类,但同一进程空间中的自动化代码可以直接使用自动化对等类。

内置的自动化对等类

如果元素接受来自用户的界面活动,或者包含屏幕阅读器应用程序的用户所需的信息,这样的元素实现了自动化对等类。并非所有的 WPF 可视化元素都有自动化对等类。实现自动化对等类的类的例子有 ButtonTextBoxLabel。未实现自动化对等类的类的例子有派生自 Decorator 的类(如 Border)和基于 Panel 的类(如 GridCanvas)。

Control 基类没有对应的对等类。如果需要一个对等类以便与派生自 Control 的自定义控件对应,则应从 FrameworkElementAutomationPeer 派生自定义对等类。

派生对等类的安全性注意事项

自动化对等类必须在部分信任环境中运行。UIAutomationClient 程序集中的代码未配置为在部分信任环境中运行,因而自动化对等类代码不应引用该程序集。您应改用 UIAutomationTypes 程序集中的类。例如,您应使用 UIAutomationTypes 程序集中的 AutomationElementIdentifiers 类,该类对应于 UIAutomationClient 程序集中的 AutomationElement 类。可以放心地在自动化对等类代码中引用 UIAutomationTypes 程序集。

对等类导航

找到自动化对等类后,进程内代码可以通过调用该对象的 GetChildrenGetParent 方法在对等类树中导航。对等类的 GetChildrenCore 方法的实现支持在控件内的 WPF 元素之间导航。UI 自动化系统调用此方法来构建控件中所包含子元素(如列表框中的列表项)的树。默认的 UIElementAutomationPeer.GetChildrenCore 方法通过遍历元素的可视化树来构建自动化对等类的树。自定义控件会重写此方法以向自动化客户端公开子元素,同时返回传达信息或允许用户交互的元素的自动化对等类。

派生对等类中的自定义项

派生自 UIElementContentElement 的所有类都包含受保护的虚方法 OnCreateAutomationPeer。WPF 调用 OnCreateAutomationPeer 以获取每个控件的自动化对等类对象。自动化代码可以使用对等类来获得关于每个控件的特性和功能的信息,并可以使用对等类来模拟交互使用。支持自动化的自定义控件必须重写 OnCreateAutomationPeer 并返回派生自 AutomationPeer 的类的实例。例如,如果一个自定义控件派生自 ButtonBase 类,则 OnCreateAutomationPeer 返回的对象应派生自 ButtonBaseAutomationPeer

实现自定义控件时,必须重写来自自动化基对等类的“核心”方法,这些方法描述了对该自定义控件唯一且特定于该自定义控件的行为。

重写 OnCreateAutomationPeer

请为自定义控件重写 OnCreateAutomationPeer 方法,以使它返回必须直接或间接从 AutomationPeer 派生的提供程序对象。

重写 GetPattern

自动化对等类简化了服务器端 UI 自动化提供程序的一些实现方面,但自定义控件的自动化对等类仍必须处理模式接口。与非 WPF 提供程序类似,对等类通过在 System.Windows.Automation.Provider 命名空间(如 IInvokeProvider)中提供接口的实现来支持控件模式。控件模式接口可通过对等类自身或其他对象实现。GetPattern 的对等类实现返回支持指定模式的对象。UI 自动化代码调用 GetPattern 方法并指定 PatternInterface 枚举值。重写后的 GetPattern 应返回实现指定模式的对象。如果控件没有自定义的模式实现,那么可以调用 GetPattern 的基类型实现来检索其实现,如果此控件类型不支持该模式,则会检索到 null。例如,可以将一个自定义 NumericUpDown 控件设置为某一范围内的值,以便其 UI 自动化对等类实现 IRangeValueProvider 接口。下面的示例说明了如何重写对等类的 GetPattern 方法以响应 PatternInterface.RangeValue 值。

public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    return base.GetPattern(patternInterface);
}

GetPattern 方法还可以指定子元素作为模式提供程序。下面的代码说明 ItemsControl 如何将 Scroll 模式处理转交给其内部 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);
}

为了指定用于模式处理的子元素,此代码获取相应的子元素对象,使用 CreatePeerForElement 方法创建一个对等类,将新对等类的 EventsSource 属性设置为当前对等类,然后返回新对等类。通过设置子元素的 EventsSource,可防止该子元素显示在自动化对等类树中并将该子元素引发的所有事件都指定为源自 EventsSource 中指定的控件。ScrollViewer 控件不显示在自动化树中,并且它所生成的滚动事件会显示为源自 ItemsControl 对象。

重写“核心”方法

自动化代码通过调用对等类的公共方法来获取有关控件的信息。若要提供有关控件的信息,请在您的控件实现不同于自动化基对等类提供的实现时,重写名称以“Core”(核心)结尾的每个方法。您的控件至少必须实现 GetClassNameCoreGetAutomationControlTypeCore 方法,如下例中所示。

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

protected override AutomationControlType GetAutomationControlTypeCore()
{
    return AutomationControlType.Spinner;
}

您的 GetAutomationControlTypeCore 实现通过返回 ControlType 值来描述您的控件。尽管您可以返回 ControlType.Custom,但您还是应返回更为具体的控件类型之一(如果它能准确地描述您的控件)。返回值为 ControlType.Custom 时,提供程序需要完成额外的工作才能实现 UI 自动化,并且 UI 自动化客户端产品无法预期控件结构、键盘交互和可能的控件模式。

实现 IsContentElementCoreIsControlElementCore 方法可指示控件是否包含数据内容以及/或者是否在用户界面中履行交互职责。默认情况下,这两个方法都返回 true。这些设置提高了自动化工具(如屏幕阅读器)的可用性,自动化工具可以使用这些方法来筛选自动化树。如果 GetPattern 方法将模式处理转交给子元素对等类,则该子元素对等类的 IsControlElementCore 方法可能会返回 false 以便在自动化树中隐藏该子元素对等类。例如,ListBox 中的滚动行为是由 ScrollViewer 处理的,并且 PatternInterface.Scroll 的自动化对等类由与 ListBoxAutomationPeer 关联的 ScrollViewerAutomationPeerGetPattern 方法返回。因此,ScrollViewerAutomationPeerIsControlElementCore 方法返回 false,以使 ScrollViewerAutomationPeer 不在自动化树中显示。

您的自动化对等类应为您的控件提供适当的默认值。请注意,引用您的控件的 XAML 可以通过包含 AutomationProperties 属性来重写核心方法的对等类实现。例如,下面的 XAML 创建了一个具有两个自定义 UI 自动化属性的按钮。

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

实现模式提供程序

如果所属元素直接派生自 Control,那么将显式声明由自定义提供程序实现的接口。例如,以下代码为实现范围值的 Control 声明一个对等类。

public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }

如果所属控件派生自特定类型的控件(如 RangeBase),那么对等类可以从等效的派生对等类派生而来。在这种情况下,对等类将派生自提供 IRangeValueProvider 基实现的 RangeBaseAutomationPeer。下面的代码演示对此类对等类的声明。

public class RangePeer2 : RangeBaseAutomationPeer { }

有关实现示例,请参见 带有主题和 UI 自动化支持的 NumericUpDown 自定义控件示例

引发事件

自动化客户端可以订阅自动化事件。自定义控件必须通过调用 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);
    }
}

请参见

任务

带有主题和 UI 自动化支持的 NumericUpDown 自定义控件示例

测试脚本生成器示例

概念

UI 自动化概述

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

修订记录

日期

History

原因

2008 年 7 月

新增主题。

信息补充。