UI 自动化提供了一个通用的统一接口,自动化客户端可以用来检查或操作各种平台和框架的用户界面。 UI 自动化使质量保证(测试)代码和辅助功能应用程序(如屏幕阅读器)能够检查用户界面元素,并从其他代码模拟用户交互。 有关跨所有平台的 UI 自动化的信息,请参阅辅助功能。
本主题介绍如何为在 WPF 应用程序中运行的自定义控件实现服务器端 UI 自动化提供程序。 WPF 通过对等自动化对象的树支持 UI 自动化,这些对象与用户界面元素树并行。 提供辅助功能的测试代码和应用程序可以直接使用自动化对等对象(适用于进程内代码),或通过 UI 自动化所提供的通用接口来实现。
自动化对等类
WPF 控件通过派生自 AutomationPeer的对等类树支持 UI 自动化。 按照约定,对等类名以控件类名开头,以“AutomationPeer”结尾。 例如, ButtonAutomationPeer 控件类的 Button 对等类。 对等类大致等效于 UI 自动化控件类型,但特定于 WPF 元素。 通过 UI 自动化接口访问 WPF 应用程序的自动化代码不会直接使用自动化对等,但同一进程空间中的自动化代码可以直接使用自动化对等。
内置自动化同级类
如果元素接受用户的接口活动,或者它们包含屏幕阅读器应用程序用户所需的信息,那么这些元素将实现一个对等自动化类。 并非所有 WPF 视觉元素都具有自动化对等。 实现自动化对等项的类的示例包括 Button、TextBox以及 Label。 不实现自动化对等的类的示例包括派生自Decorator的类,例如Border,以及基于Panel的类,例如Grid和Canvas。
基 Control 类没有相应的对等类。 如果需要一个对等类来对应于派生自 Control的自定义控件,则应从 FrameworkElementAutomationPeer中派生自定义对等类。
派生对等方的安全注意事项
自动化同级设备必须在部分信任环境中运行。 UIAutomationClient 程序集中的代码未配置为在部分信任环境中运行,自动化对等代码不应引用该程序集。 您应该改用 UIAutomationTypes 程序集中的类。 例如,应使用 AutomationElementIdentifiers UIAutomationTypes 程序集中的类,该类对应于 AutomationElement UIAutomationClient 程序集中的类。 在自动化对等代码中引用 UIAutomationTypes 程序集是安全的。
对等导航
找到自动化对等方后,进程内代码可以通过调用对象的 GetChildren 和 GetParent 方法来导航对等树。 对等方的方法实现支持控件内 WPF 元素之间的 GetChildrenCore 导航。 UI 自动化系统调用此方法以构建控件中包含的子元素树;例如,列表框中的列表项。 默认 UIElementAutomationPeer.GetChildrenCore 方法遍历元素的可视化树,以生成自动化对等树。 自定义控件重写此方法,以向自动化客户端公开子元素,返回传达信息或允许用户交互的元素的自动化对等。
派生节点中的自定义项
所有从 UIElement 和 ContentElement 派生的类都包含受保护的虚拟方法 OnCreateAutomationPeer。 WPF 调用 OnCreateAutomationPeer 以获取每个控件的自动化对等对象。 自动化代码可以使用对等方来获取有关控件的特性和功能的信息,以及模拟交互式使用。 支持自动化的自定义控件必须重写 OnCreateAutomationPeer,并返回一个派生于 AutomationPeer 的类实例。 例如,如果自定义控件派生自 ButtonBase 该类,则由此 OnCreateAutomationPeer 返回的对象应派生自 ButtonBaseAutomationPeer。
实现自定义控件时,必须重写基本自动化对等类中的“Core”方法,这些方法用来描述自定义控件特有的行为。
重写 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);
}
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”结尾的方法。 控件必须至少实现 GetClassNameCore 和 GetAutomationControlTypeCore 方法,如以下示例所示。
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,但如果控件准确描述控件,则应返回一种更具体的控件类型。 提供程序实现 UI 自动化时,返回值 ControlType.Custom 需要额外工作,而 UI 自动化客户端产品无法预测控件结构、键盘交互和可能的控件模式。
实现和IsContentElementCoreIsControlElementCore方法,以指示控件是否包含数据内容或满足用户界面(或两者)中的交互角色。 默认情况下,这两种方法都返回 true
。 这些设置可提高自动化工具(如屏幕阅读器)的可用性,这些工具可能使用这些方法来筛选自动化树。 如果您的GetPattern方法将模式处理转移到子元素同行,则子元素同行的IsControlElementCore方法可以返回 false,以便在自动化树中隐藏其自身。 例如,在ListBox中滚动由ScrollViewer处理,并且自动化对等方PatternInterface.Scroll由关联的ListBoxAutomationPeer的GetPattern方法返回。因此,ScrollViewerAutomationPeer的方法IsControlElementCore返回false
,以便ScrollViewerAutomationPeer不会出现自动化树中。
自动化伙伴应为控件提供适当的默认值。 请注意,引用您控件的 XAML 可以通过包括 AutomationProperties 属性来覆盖您对核心方法的对等实现。 例如,以下 XAML 创建一个具有两个自定义 UI 自动化属性的按钮。
<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