Автоматизация пользовательского интерфейса настраиваемого элемента управления WPF
Автоматизация пользовательского интерфейса предоставляет единый обобщенный интерфейс, который клиенты автоматизации могут использовать для изучения или эксплуатации пользовательских интерфейсов различных платформ и платформ. Автоматизация пользовательского интерфейса позволяет и коду проверки качества (тесту), и приложениям специальных возможностей, таким как средства чтения с экрана, проверять элементы пользовательского интерфейса и имитировать взаимодействие пользователей с ними из другого кода. Сведения об автоматизации пользовательского интерфейса на всех платформах см. в разделе "Специальные возможности".
В этом разделе описывается реализация поставщика автоматизации пользовательского интерфейса на стороне сервера для пользовательского элемента управления, работающего в приложении WPF. WPF поддерживает автоматизацию пользовательского интерфейса с помощью дерева объектов равноправной автоматизации, которое соответствует дереву элементов пользовательского интерфейса. Тестовый код и приложения, предоставляющие специальные возможности, могут использовать одноранговые объекты автоматизации напрямую (для внутрипроцессного кода) или через универсальный интерфейс, предоставляемый службой автоматизации пользовательского интерфейса.
Классы одноранговых узлов автоматизации
Элементы управления WPF поддерживают автоматизацию пользовательского интерфейса с помощью дерева одноранговых классов, производных от AutomationPeer. По соглашению имена одноранговых классов начинаются с имени класса элемента управления и заканчиваются именем "AutomationPeer". Например, ButtonAutomationPeer является одноранговым классом для класса управления Button. Одноранговые классы примерно эквивалентны типам элементов управления автоматизации пользовательского интерфейса, но относятся к элементам WPF. Код автоматизации, который обращается к приложениям WPF через интерфейс автоматизации пользовательского интерфейса, не использует пиринги автоматизации напрямую, но код автоматизации в том же пространстве процессов может использовать пиринги автоматизации напрямую.
Встроенные классы одноранговых узлов автоматизации
Элементы реализуют класс автоматизации однорангового взаимодействия, если они принимают интерфейсное взаимодействие от пользователя или содержат информацию, необходимую пользователям программ чтения с экрана. Не все визуальные элементы WPF имеют одноранговые узлы автоматизации. Примерами классов, реализующих одноранговые узлы автоматизации, являются Button, TextBoxи Label. Примеры классов, которые не реализуют одноранговые узлы автоматизации, являются классами, производными от Decorator, например Border, и классами на основе Panel, таких как Grid и Canvas.
Базовый Control класс не имеет соответствующего однорангового класса. Если требуется одноранговый класс для соответствия пользовательскому элементу управления, происходящему от Control, следует наследовать пользовательский одноранговый класс из FrameworkElementAutomationPeer.
Соображения по безопасности для производных одноранговых узлов
Одноранговые узлы автоматизации должны выполняться в среде с частичным доверием. Код в сборке UIAutomationClient не настроен для выполнения в среде с частичным доверием, а одноранговый код автоматизации не должен ссылаться на нее. Вместо этого следует использовать классы в сборке UIAutomationTypes. Например, следует использовать класс AutomationElementIdentifiers из сборки UIAutomationTypes, которая соответствует классу AutomationElement в сборке UIAutomationClient. Безопасно ссылаться на сборку UIAutomationTypes в одноранговом коде автоматизации.
Одноранговая навигация
После нахождения партнера автоматизации внутрипроцессный код может перемещаться по дереву партнеров, вызывая методы объекта GetChildren и GetParent. Навигация между элементами WPF в элементе управления поддерживается реализацией однорангового метода GetChildrenCore. Система автоматизации пользовательского интерфейса вызывает этот метод для создания дерева подпорядков, содержащихся в элементе управления; например, позиции в поле списка. По умолчанию метод UIElementAutomationPeer.GetChildrenCore проходит по визуальному дереву элементов, чтобы создать дерево узлов автоматизации. Пользовательские элементы управления переопределяют этот метод, чтобы представить дочерние элементы клиентам автоматизации, возвращая равноправные узлы автоматизации элементов, которые предназначены для передачи информации или взаимодействия с пользователем.
Настройки в производном одноранговом узле
Все классы, производные от UIElement и ContentElement, содержат защищенный виртуальный метод OnCreateAutomationPeer. WPF вызывает OnCreateAutomationPeer для получения объекта периферийного интерфейса автоматизации для каждого элемента управления. Код автоматизации может использовать одноранговый узел, чтобы получить информацию о функциях и характеристиках элемента управления и имитировать его интерактивное использование. Настраиваемый элемент управления, поддерживающий автоматизацию, должен переопределять OnCreateAutomationPeer и возвращать экземпляр класса, наследуемого от AutomationPeer. Например, если пользовательский элемент управления является производным от класса ButtonBase, объект, возвращаемый OnCreateAutomationPeer, должен быть производным от ButtonBaseAutomationPeer.
При реализации пользовательского элемента управления необходимо переопределить методы Core из базового однорангового класса автоматизации, описывающего поведение, уникальное и конкретное для пользовательского элемента управления.
Переопределите OnCreateAutomationPeer
Переопределите метод OnCreateAutomationPeer для пользовательского элемента управления, чтобы он возвращал объект поставщика, который должен быть напрямую или косвенно производным от AutomationPeer.
Переопределите GetPattern
Одноранговые узлы автоматизации упрощают некоторые аспекты реализации поставщиков автоматизации пользовательского интерфейса на стороне сервера, но одноранговые узлы автоматизации пользовательского интерфейса по-прежнему должны обрабатывать интерфейсы шаблонов. Как и независимые от WPF поставщики, одноранговые узлы поддерживают шаблоны управления, предоставляя реализации интерфейсов, таких как IInvokeProvider, в пространстве имен System.Windows.Automation.Provider. Интерфейсы шаблона элемента управления могут быть реализованы самим элементом или другим объектом. Реализация GetPattern однорангового узла возвращает объект, поддерживающий указанный шаблон. Код автоматизации пользовательского интерфейса вызывает метод GetPattern и задает значение перечисления PatternInterface. Переопределение GetPattern должно возвращать объект, реализующий указанный шаблон. Если элемент управления не имеет настраиваемой реализации шаблона, можно вызвать реализацию базового типа GetPattern, чтобы получить ее реализацию или null, если шаблон не поддерживается для этого типа элемента управления. Например, настраиваемому элементу управления NumericUpDown можно задать значение в определенном диапазоне, вследствие чего его одноранговый элемент автоматизации пользовательского интерфейса будет реализовывать интерфейс 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
Код автоматизации получает информацию о вашем контроле, вызывая общедоступные методы класса-партнёра. Чтобы предоставить сведения об элементе управления, переопределите каждый метод, имя которого заканчивается "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, вы должны вернуть один из более конкретных типов элементов управления, если он точно описывает элемент управления. Возвращаемое значение ControlType.Custom требует дополнительной работы для поставщика для реализации автоматизации пользовательского интерфейса, а клиентские продукты автоматизации пользовательского интерфейса не могут предвидеть структуру управления, взаимодействие с клавиатурой и возможные шаблоны элементов управления.
Реализуйте методы IsContentElementCore и IsControlElementCore, чтобы указать, содержит ли элемент управления содержимое данных или выполняет интерактивную роль в пользовательском интерфейсе (или обоих). По умолчанию оба метода возвращают true
. Эти параметры повышают удобство использования таких средств автоматизации, как средства чтения с экрана, которые могут использовать эти методы для фильтрации дерева автоматизации. Если метод GetPattern передает обработку шаблонов одноранговому узлу подэлемента, метод IsControlElementCore этого узла может вернуть значение false, чтобы скрыть этот узел из дерева автоматизации. Например, прокрутка в ListBox обрабатывается ScrollViewer, а автоматизационный узел для PatternInterface.Scroll возвращается методом GetPatternScrollViewerAutomationPeer, связанным с ListBoxAutomationPeer. Поэтому метод IsControlElementCoreScrollViewerAutomationPeer возвращает false
, чтобы ScrollViewerAutomationPeer не отображался в дереве автоматизации.
Одноранговый узел автоматизации должен предоставлять соответствующие значения по умолчанию для элемента управления. Обратите внимание, что XAML, ссылающийся на элемент управления, может переопределить одноранговые реализации основных методов, включая AutomationProperties атрибуты. Например, следующий КОД XAML создает кнопку с двумя настраиваемыми свойствами автоматизации пользовательского интерфейса.
<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
Пример реализации см. в исходном коде C# или Visual Basic, реализующем и использующем настраиваемый элемент управления 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);
}
}
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
См. также
- Обзор автоматизации пользовательского интерфейса
- Server-Side внедрение автоматизации для поставщиков пользовательского интерфейса
- Пользовательский элемент управления NumericUpDown (C#) на GitHub
- Пользовательский элемент управления NumericUpDown (Visual Basic) на GitHub
.NET Desktop feedback