Поделиться через


Настраиваемые одноранговые элементы автоматизации

В этой статье объясняется, как использовать одноранговые узлы автоматизации в службе автоматизации пользовательского интерфейса Майкрософт и как обеспечить надежную поддержку автоматизации для пользовательских классов пользовательского интерфейса.

Замечание

В этом руководстве используются пространства имен и ссылки WinUI 3 и пакета Windows App SDK на основе Microsoft.UI.Xaml.Automation.Peers.AutomationPeer.

Автоматизация пользовательского интерфейса предоставляет платформу, используемую клиентами автоматизации для проверки и взаимодействия с пользовательским интерфейсом на разных платформах. В приложениях Windows большинство встроенных элементов управления уже поддерживают автоматизацию пользовательского интерфейса. При наследовании нового элемента управления или типа поддержки из существующих незапечатанных классов, вы можете добавить поведение, которое не представлено посредником по умолчанию. В этом случае расширьте поддержку автоматизации, исходя из соответствующего automationPeer, добавив требуемое поведение поставщика и гарантируя, что инфраструктура управления создает свой одноранговый узел.

Автоматизация пользовательского интерфейса поддерживает средства специальных возможностей (например, средства чтения с экрана) и автоматизированные системы тестирования. В обоих сценариях внешний код может проверять элементы пользовательского интерфейса и имитировать взаимодействие с приложением. Общие сведения о платформе см. в разделе "Обзор службы автоматизации пользовательского интерфейса".

Для команд, которые уделяют внимание доступности и автоматизированному тестированию, партнеры по автоматизации также являются гарантией надежности. Стабильная структура автоматизации и точную поддержку шаблонов повышают поведение вспомогательных технологий и долгосрочную стабильность тестирования.

Существует две основные аудитории для автоматизации пользовательского интерфейса.

  • Клиенты автоматизации пользовательского интерфейса вызывают API автоматизации пользовательского интерфейса для обнаружения и взаимодействия с доступным в настоящее время пользовательским интерфейсом. Например, средство чтения с экрана является клиентом службы автоматизации пользовательского интерфейса. Клиенты перемещаются по дереву элементов автоматизации и могут нацелиться на одно приложение или полное дерево настольных компьютеров.
  • Поставщики автоматизации пользовательского интерфейса публикуют сведения в этом дереве, реализуя API для элементов управления, представленных приложением. При создании кастомного элемента управления вы участвуете в провайдерской модели и должны убедиться, что ваш элемент управления подходит как для доступности, так и для тестовых клиентов.

Платформа обычно предоставляет параллельные поверхности API: один для клиентов и один для поставщиков. В этом разделе рассматривается расширяемость поставщика, особенно одноранговые классы и интерфейсы поставщика. Клиентские API упоминаются только в контексте. Для получения инструкций по работе с клиентом см. в руководстве пользователя по программированию службы автоматизации пользовательского интерфейса.

Замечание

Клиенты автоматизации пользовательского интерфейса обычно являются классическими приложениями и обычно не используют управляемый код. Автоматизация пользовательского интерфейса основана на стандарте, а не на конкретной реализации или платформе. Многие существующие клиенты автоматизации пользовательского интерфейса, включая вспомогательные технологии, такие как средства чтения с экрана, используют интерфейсы объектной модели компонентов (COM) для взаимодействия с автоматизацией пользовательского интерфейса, системой и приложениями, которые выполняются в дочерних окнах. Дополнительные сведения о COM-интерфейсах и создании клиента автоматизации пользовательского интерфейса с помощью COM см. в разделе "Основы автоматизации пользовательского интерфейса".

Определение текущего состояния поддержки UI Automation для вашего пользовательского класса UI

Перед реализацией пользовательского однорангового узла автоматизации убедитесь, что базовый элемент управления и его одноранговый узел уже предоставляют необходимую поддержку. Во многих случаях FrameworkElementAutomationPeer, одноранговые элементы управления и их встроенные шаблоны достаточны. Потребность в настройке зависит от того, сколько ваши элементы управления расходятся из базовой объектной модели, а также от того, будут ли изменения шаблона или взаимодействия вводить новый пользовательский интерфейс, требующий дополнительной поддержки специальных возможностей.

В рамках этой оценки убедитесь, что поведение однорангового узла по умолчанию достаточно для автоматических тестов специальных возможностей, а не только ручной проверки. Если тестовые сценарии зависят от определенных шаблонов, имен или иерархии, реализуйте кастомный компонент, чтобы эти ожидания оставались явными и поддерживаемыми.

Даже если базовое поведение функционально приемлемо, все еще рекомендуется определять собственного партнёра, когда имеет значение точность отчётности ClassName для автоматизации и надежности тестирования, особенно для элементов управления, предназначенных для повторного использования другими.

Классы одноранговых узлов автоматизации

Платформы WinUI и связанные фреймворки XAML основаны на установленных соглашениях об автоматизации UI из предыдущих управляемых UI стеков, таких как Windows Forms, WPF и Silverlight. Многие концепции элементов управления и одноранговых узлов соответствуют этим установленным шаблонам.

По соглашению имена одноранговых классов начинаются с имени класса элемента управления и заканчиваются именем "AutomationPeer". Например, ButtonAutomationPeer — это одноранговый класс для класса элемента управления Button .

Замечание

В этой статье мы рассмотрим свойства, связанные с специальными возможностями, как более важные при реализации однорангового узла управления. Но для более общей концепции поддержки автоматизации пользовательского интерфейса следует реализовать одноранговый узел в соответствии с рекомендациями, описанными в руководстве программиста по автоматизации пользовательского интерфейса и основах автоматизации пользовательского интерфейса. В этих разделах не рассматриваются определенные API AutomationPeer , используемые в WinUI и пакете SDK для приложений Windows, но они описывают свойства, которые определяют класс или предоставляют другую информацию или взаимодействие.

Одноранговые элементы, шаблоны и типы элементов управления

Шаблон элемента управления — это реализация интерфейса, которая предоставляет определенный аспект функциональности элемента управления клиенту автоматизации пользовательского интерфейса. Клиенты автоматизации пользовательского интерфейса используют свойства и методы, предоставляемые с помощью шаблона элемента управления, для получения сведений о возможностях элемента управления или управления поведением элемента управления во время выполнения.

Шаблоны элементов управления обеспечивают поведение независимо от визуальных компонентов или идентификации специфического класса. Например, табличный элемент управления может предоставлять сетку , чтобы клиенты могли запрашивать измерения и извлекать элементы. Клиенты могут использовать Вызов для вызываемых элементов управления (например, кнопок) и Прокрутки для прокручиваемых поверхностей (например, элементов управления списком). Шаблоны доступны для создания, поэтому один элемент управления может предоставлять несколько действий.

Шаблоны элементов управления относятся к пользовательскому интерфейсу, так как интерфейсы связаны с COM-объектами. В COM можно запросить объект, чтобы узнать, какие интерфейсы она поддерживает, а затем использовать эти интерфейсы для доступа к функциям. В службе автоматизации пользовательского интерфейса клиенты автоматизации пользовательского интерфейса могут запрашивать элемент автоматизации пользовательского интерфейса, чтобы узнать, какие шаблоны элементов управления он поддерживает, а затем взаимодействовать с элементом и его одноранговым элементом управления с помощью свойств, методов, событий и структур, предоставляемых поддерживаемыми шаблонами элементов управления.

Одной из основных обязанностей однорангового узла автоматизации является объявление поддерживаемых шаблонов. В этом случае провайдеры переопределяют GetPatternCore, который обеспечивает работу GetPattern. Клиенты запрашивают один шаблон за раз; Если он поддерживается, одноранговый узел возвращает объект (обычно сам по себе), в противном случае возвращает значение NULL.

Тип элемента управления широко классифицирует функциональные возможности, представленные одноранговым элементом. Это отличается от шаблона элемента управления: шаблоны описывают определенные возможности, а тип элемента управления описывает роль более высокого уровня. Каждый тип элемента управления содержит рекомендации по следующим областям:

  • Шаблоны элементов управления автоматизации пользовательского интерфейса: тип элемента управления может поддерживать несколько шаблонов, каждый из которых представляет другую классификацию сведений или взаимодействия. Каждый тип элемента управления имеет набор шаблонов элементов управления, которые элемент управления должен поддерживать, набор, который является необязательным, и набор, который элемент управления не должен поддерживать.
  • Значения свойств автоматизации пользовательского интерфейса: каждый тип элемента управления имеет набор свойств, которые элемент управления должен поддерживать. Это общие свойства, как описано в обзоре свойств автоматизации пользовательского интерфейса, а не те, которые относятся к шаблону.
  • События автоматизации пользовательского интерфейса. Каждый тип элемента управления имеет набор событий, которые элемент управления должен поддерживать. Опять же, они являются общими, а не шаблонными, как описано в обзоре событий автоматизации пользовательского интерфейса.
  • Структура дерева автоматизации пользовательского интерфейса: каждый тип элемента управления определяет, как элемент управления должен отображаться в структуре дерева автоматизации пользовательского интерфейса.

Независимо от сведений о платформе поведение клиента не привязано к определенной модели приложения XAML. Многие существующие клиенты, включая вспомогательные технологии, взаимодействуют через COM. В этой модели клиенты QueryInterface для интерфейсов шаблонов или общих интерфейсов автоматизации, и инфраструктура автоматизации управляет вызовами к поставщику и одноранговой реализации вашего приложения.

При реализации шаблонов элементов управления для приложения Windows с управляемым кодом можно использовать интерфейсы .NET для представления этих шаблонов вместо синтаксиса COM-интерфейса. Например, интерфейс шаблона автоматизации пользовательского интерфейса для реализации поставщика .NET шаблона InvokeIInvokeProvider.

Список шаблонов элементов управления, интерфейсов поставщиков и их назначения см. в разделе "Шаблоны элементов управления" и "Интерфейсы". Сведения о типах элементов управления см. в разделе "Общие сведения о типах элементов управления автоматизации пользовательского интерфейса".

Руководство по реализации шаблонов элементов управления

Руководство по шаблону управления относится к общей платформе автоматизации пользовательского интерфейса, а не только к определенной модели приложения. При реализации шаблонов выравнивайте поведение с помощью документации Майкрософт и соглашений об автоматизации пользовательского интерфейса. Начните с внедрения шаблонов управления для автоматизации пользовательского интерфейса, уделяя особое внимание разделам внедрения и обязательных элементов для каждого шаблона. Хотя эти ссылки часто описывают собственные com-интерфейсы, эквивалентные интерфейсы поставщика доступны для WinUI в Microsoft.UI.Xaml.Automation.Provider.

Если вы используете одноранговые узлы автоматизации по умолчанию и расширяете их поведение, эти одноранговые узлы уже написаны в соответствии с рекомендациями по автоматизации пользовательского интерфейса. Если они поддерживают шаблоны элементов управления, вы можете полагаться на эту поддержку шаблона, соответствующую рекомендациям по реализации шаблонов элементов управления автоматизации пользовательского интерфейса. Если одноранговый элемент управления сообщает, что он представляет тип, определённый в автоматизации пользовательского интерфейса, то он следует руководству, описанному в статье "Поддержка типов элементов управления автоматизации пользовательского интерфейса.

Дополнительная интерпретация часто требуется при реализации шаблонов или типов элементов управления, не охватываемых элементами управления по умолчанию. Например, элементы управления XAML по умолчанию не реализуют шаблоны заметок. Если ваше приложение зависит от процессов аннотации, ваш коллега может реализовать IAnnotationProvider и сообщить тип элемента управления Document со свойствами, описывающими возможности для заметок.

Рекомендуется использовать рекомендации, которые вы видите для шаблонов в разделе "Реализация шаблонов элементов управления автоматизации пользовательского интерфейса " или типов элементов управления в разделе "Поддержка типов элементов управления автоматизации пользовательского интерфейса " в качестве ориентации и общих рекомендаций. Ссылки API содержат описания и примечания по назначению API. Для конкретных синтаксисов WinUI используйте эквивалентный API в пространстве имен Microsoft.UI.Xaml.Automation.Provider .

Встроенные классы одноранговых узлов автоматизации

Как правило, элементы делают доступными одноранговые узлы автоматизации, когда они являются интерактивными или предоставляют значимые сведения для вспомогательных технологий. Не все визуальные элементы нуждаются в одноранговых элементах. Например, Button и TextBox имеют аналоги, а типы макетов на основе Border и Panel, такие как Grid и Canvas, не имеют. Панель вносит свой вклад только в макет и не имеет прямой модели взаимодействия с доступностью, поэтому её значимые дочерние элементы отображаются через ближайшего предка, имеющего доступное представление.

Границы процесса автоматизации пользовательского интерфейса

Клиенты автоматизации пользовательского интерфейса, которые проверяют приложения Windows, обычно выполняются в отдельном процессе. Инфраструктура автоматизации пользовательского интерфейса обрабатывает межпроцессное взаимодействие. Дополнительные сведения см. в разделе "Основы автоматизации пользовательского интерфейса".

OnCreateAutomationPeer

Все классы, производные от UIElement , содержат защищенный виртуальный метод OnCreateAutomationPeer. Последовательность инициализации объектов для одноранговых узлов автоматизации вызывает OnCreateAutomationPeer , чтобы получить одноранговый объект службы автоматизации для каждого элемента управления и таким образом создать дерево автоматизации пользовательского интерфейса для использования во время выполнения. Код автоматизации пользовательского интерфейса может использовать "peer" для получения информации о характеристиках и возможностях элемента управления, а также для имитации интерактивного использования через шаблоны управления. Настраиваемый элемент управления, поддерживающий автоматизацию, должен переопределить OnCreateAutomationPeer и вернуть экземпляр класса, наследуемого от AutomationPeer. Например, если пользовательский элемент управления является производным от класса ButtonBase , объект, возвращаемый OnCreateAutomationPeer , должен быть производным от ButtonBaseAutomationPeer.

Если вы пишете пользовательский элемент управления и предоставляете пользовательский одноранговый узел, переопределите OnCreateAutomationPeer , чтобы он вернул новый экземпляр своего типа однорангового узла. Пеер должен быть производным напрямую или косвенно от AutomationPeer.

Например, следующий код объявляет, что пользовательский элемент управления NumericUpDown используется NumericUpDownPeer для автоматизации пользовательского интерфейса.

using Microsoft.UI.Xaml.Automation.Peers;
...
public class NumericUpDown : RangeBase {
    public NumericUpDown() {
    // other initialization; DefaultStyleKey etc.
    }
    ...
    protected override AutomationPeer OnCreateAutomationPeer()
    {
        return new NumericUpDownAutomationPeer(this);
    }
}
Public Class NumericUpDown
    Inherits RangeBase
    ' other initialization; DefaultStyleKey etc.
       Public Sub New()
       End Sub
       Protected Overrides Function OnCreateAutomationPeer() As AutomationPeer
              Return New NumericUpDownAutomationPeer(Me)
       End Function
End Class
// NumericUpDown.idl
namespace MyNamespace
{
    runtimeclass NumericUpDown : Microsoft.UI.Xaml.Controls.Primitives.RangeBase
    {
        NumericUpDown();
        Int32 MyProperty;
    }
}

// NumericUpDown.h
...
struct NumericUpDown : NumericUpDownT<NumericUpDown>
{
    ...
    Microsoft::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer()
    {
        return winrt::make<MyNamespace::implementation::NumericUpDownAutomationPeer>(*this);
    }
};
//.h
public ref class NumericUpDown sealed : Microsoft::UI::Xaml::Controls::Primitives::RangeBase
{
// other initialization not shown
protected:
    virtual AutomationPeer^ OnCreateAutomationPeer() override
    {
         return ref new NumericUpDownAutomationPeer(this);
    }
};

Замечание

Реализация OnCreateAutomationPeer не должна делать ничего больше, чем инициализировать новый экземпляр пользовательского узла автоматизации, передавая вызываемый элемент управления в качестве владельца и вернув этот экземпляр. Не пытайтесь выполнять дополнительные логические операции в этом методе. В частности, любая логика, которая может привести к уничтожению AutomationPeer в одном вызове, может привести к неожиданному поведению среды выполнения.

В типичных реализациях OnCreateAutomationPeerвладелец является этим или Me, так как переопределение выполняется в области экземпляра элемента управления.

Одноранговые классы можно определить в том же файле, что и элемент управления или в отдельных файлах. Типы одноранговых узлов платформы живут в Microsoft.UI.Xaml.Automation.Peers, но пользовательские одноранговые узлы могут жить в любом пространстве имен, если на них ссылаются необходимые пространства имен, где реализуется OnCreateAutomationPeer .

Выбор правильного базового однорангового класса

Произведите наследование пользовательского AutomationPeer от ближайшего базового класса, соответствующего функциональному базовому классу вашего элемента управления. В предыдущем примере NumericUpDown наследует от RangeBase, поэтому RangeBaseAutomationPeer является правильной базой для однорангового класса. Это позволяет повторно использовать существующее поведение IRangeValueProvider , а не повторно реализовать его.

Базовый класс Control не имеет соответствующего однорангового класса. Если требуется одноранговый класс для соответствия пользовательскому элементу управления, наследуемому из Control, наследуется пользовательский одноранговый класс из FrameworkElementAutomationPeer.

Если вы наследуете от ContentControl напрямую, этот класс не имеет поведения пир-узла автоматизации по умолчанию, так как отсутствует реализация OnCreateAutomationPeer, которая ссылается на пир-класс. Поэтому не забудьте реализовать OnCreateAutomationPeer для использования собственного однорангового узла или использовать FrameworkElementAutomationPeer в качестве однорангового узла, если этот уровень поддержки специальных возможностей подходит для вашего элемента управления.

Замечание

Обычно вы не наследуете от AutomationPeer , а не FrameworkElementAutomationPeer. Если вы сделали производный напрямую от AutomationPeer, вам потребуется дублировать большинство базовой поддержки специальных возможностей, которая обычно предоставляется FrameworkElementAutomationPeer.

Инициализация пользовательского однорангового класса

Пользовательский одноранговый элемент должен предоставлять типобезопасный конструктор, который принимает управляющий элемент владельца и передает его инициализатору базового класса. В следующем примере owner передается RangeBaseAutomationPeer, а в конечном итоге FrameworkElementAutomationPeer использует это значение для задания FrameworkElementAutomationPeer.Owner.

public NumericUpDownAutomationPeer(NumericUpDown owner): base(owner)
{}
Public Sub New(owner As NumericUpDown)
    MyBase.New(owner)
End Sub
// NumericUpDownAutomationPeer.idl
import "NumericUpDown.idl";
namespace MyNamespace
{
    runtimeclass NumericUpDownAutomationPeer : Microsoft.UI.Xaml.Automation.Peers.AutomationPeer
    {
        NumericUpDownAutomationPeer(NumericUpDown owner);
        Int32 MyProperty;
    }
}

// NumericUpDownAutomationPeer.h
...
struct NumericUpDownAutomationPeer : NumericUpDownAutomationPeerT<NumericUpDownAutomationPeer>
{
    ...
    NumericUpDownAutomationPeer(MyNamespace::NumericUpDown const& owner);
};
//.h
public ref class NumericUpDownAutomationPeer sealed :  Microsoft::UI::Xaml::Automation::Peers::RangeBaseAutomationPeer
//.cpp
public:    NumericUpDownAutomationPeer(NumericUpDown^ owner);

Основные методы AutomationPeer

Переопределяемые методы автоматизации предоставляются парами: общедоступный метод, используемый инфраструктурой поставщика, а также защищенный Core метод, предназначенный для настройки. По умолчанию общедоступный метод перенаправляется в соответствующий Core метод, возвращаясь к базовой реализации при необходимости.

При реализации однорангового узла для пользовательского элемента управления переопределяют Core методы, где бы ваше поведение не отличалось от базового поведения однорангового узла. Служба автоматизации пользовательского интерфейса получает информацию через общедоступные методы, но точкой вашей настройки является соответствующее Core переопределение.

По крайней мере при определении нового однорангового класса реализуйте метод GetClassNameCore , как показано в следующем примере.

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

Замечание

Возможно, вы захотите сохранить строки как константы, а не в теле метода, но это зависит от вас. Для GetClassNameCore не потребуется локализовать эту строку. Свойство LocalizedControlType используется в любой момент, когда локализованная строка требуется клиенту автоматизации пользовательского интерфейса, а не ClassName.

GetAutomationControlType

Некоторые вспомогательные технологии сообщают GetAutomationControlType вместе с именем автоматизации пользовательского интерфейса. Если роль вашего элемента управления существенно отличается от роли базового класса, создайте кастомный объект и переопределите GetAutomationControlTypeCore. Это особенно важно при производных от обобщенных баз, таких как ItemsControl или ContentControl.

Реализация GetAutomationControlTypeCore описывает ваш элемент управления, возвращая значение AutomationControlType. Хотя вы можете вернуть AutomationControlType.Custom, вы должны вернуть один из более конкретных типов элементов управления, если он точно описывает основные сценарии элемента управления. Приведем пример.

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

Замечание

Если вы не укажете AutomationControlType.Custom, вам не нужно реализовать GetLocalizedControlTypeCore для предоставления значения свойства LocalizedControlType клиентам. Общая инфраструктура автоматизации пользовательского интерфейса предоставляет переведенные строки для всех возможных значений AutomationControlType , отличных от AutomationControlType.Custom.

GetPattern и GetPatternCore

GetPatternCore возвращает объект, поддерживающий запрошенный шаблон. Клиент службы автоматизации пользовательского интерфейса запрашивает конкретный PatternInterface через GetPattern. Если поддерживается, переопределение должно возвращать объект реализации, обычно сам одноранговый узел. Если не поддерживается, возвращает значение NULL (часто делегируя базовое поведение и позволяя возвращать значение NULL).

Если шаблон поддерживается, GetPatternCore может вернуть это или me. Затем клиенты приводят возвращаемое значение GetPattern к запрашиваемому интерфейсу.

Если одноранговый класс наследует от другого однорангового класса, и вся необходимая поддержка и отчеты о шаблонах уже обрабатываются базовым классом, реализация GetPatternCore не требуется. Например, если вы реализуете элемент управления диапазоном, производный от RangeBase, и одноранговый узел является производным от RangeBaseAutomationPeer, этот одноранговый узел возвращает себя для PatternInterface.RangeValue и имеет рабочие реализации интерфейса IRangeValueProvider, который поддерживает данный шаблон.

Хотя это не литеральный код, этот пример примерно соответствует реализации GetPatternCore, уже присутствующей в RangeBaseAutomationPeer.

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

Если вы реализуете одноранговый узел, где у вас нет всей поддержки, необходимой из базового однорангового класса, или вы хотите изменить или добавить в набор базовых наследуемых шаблонов, которые может поддерживать ваш одноранговый узел, необходимо переопределить GetPatternCore , чтобы клиенты автоматизации пользовательского интерфейса могли использовать шаблоны.

Список шаблонов поставщиков автоматизации пользовательского интерфейса, поддерживаемых WinUI, см. в разделе Microsoft.UI.Xaml.Automation.Provider. Каждый такой шаблон имеет соответствующее значение перечисления PatternInterface , которое заключается в том, как клиенты автоматизации пользовательского интерфейса запрашивают шаблон в вызове GetPattern .

Одноранговый узел может заявлять о поддержке нескольких шаблонов. В этом случае переопределение должно включать логику возвращаемого пути для каждого поддерживаемого значения PatternInterface и возвращать одноранговый узел в каждом случае сопоставления. Ожидается, что вызывающий объект запрашивает только один интерфейс за раз, и вызывающий объект будет выполнять приведение к ожидаемому интерфейсу.

Ниже приведен пример переопределения GetPatternCore для пользовательского однорангового узла. Он сообщает о поддержке двух шаблонов, IRangeValueProvider и IToggleProvider. Здесь представлен контрол мультимедийного отображения, который может отображаться в полноэкранном режиме (режим переключения) и имеет полосу текущего состояния, в которой пользователи могут выбрать позицию (элемент управления диапазоном). Этот код был получен из примера специальных возможностей XAML (архивированный пример прежних версий).

protected override object GetPatternCore(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    else if (patternInterface == PatternInterface.Toggle)
    {
        return this;
    }
    return null;
}

Перенаправление паттернов из подэлементов

Реализация метода GetPatternCore также может указывать вложенный элемент или часть в качестве поставщика шаблонов для своего хоста. В этом примере показано, как ItemsControl передает обработку шаблонов прокрутки в одноранговый узел внутреннего элемента управления ScrollViewer . Чтобы указать подэлемент для обработки шаблонов, этот код получает объект подэлемента, создает одноранговый элемент для подэлемента с помощью метода FrameworkElementAutomationPeer.CreatePeerForElement и возвращает новый одноранговый элемент.

protected override object GetPatternCore(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.Scroll)
    {
        ItemsControl owner = (ItemsControl) base.Owner;
        UIElement itemsHost = owner.ItemsHost;
        ScrollViewer element = null;
        while (itemsHost != owner)
        {
            itemsHost = VisualTreeHelper.GetParent(itemsHost) as UIElement;
            element = itemsHost as ScrollViewer;
            if (element != null)
            {
                break;
            }
        }
        if (element != null)
        {
            AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement(element);
            if ((peer != null) && (peer is IScrollProvider))
            {
                return (IScrollProvider) peer;
            }
        }
    }
    return base.GetPatternCore(patternInterface);
}

Другие основные методы

Ваш элемент управления может нуждаться в альтернативных вариантах клавиатуры для основных сценариев; дополнительные сведения см. в разделе Специальные возможности клавиатуры. Поведение ключа относится к логике управления, но одноранговый узел должен сообщать метаданные ключа через GetAcceleratorKeyCore и GetAccessKeyCore. Если ключевые описания ориентированы на пользователей, локализуйте их с помощью ресурсов.

Если элемент управления предоставляет коллекции, используйте функциональные и одноранговые базовые классы, которые уже реализуют поведение коллекции. В противном случае переопределите GetChildrenCore, чтобы точно представлять отношения родительско-дочерних элементов в дереве автоматизации.

Реализуйте IsContentElementCore и IsControlElementCore , чтобы указать, следует ли рассматривать элемент управления как содержимое, элемент управления или оба элемента управления. Оба значения по умолчанию равны true. Эти значения помогают клиентам, таким как программы экранного чтения, эффективно фильтровать и перемещаться по дереву. Если метод GetPatternCore перенаправляет поведение к одноранговому подэлементу, этот подэлемент может вернуть false, IsControlElementCore чтобы оставаться вне видимого дерева.

Некоторые элементы управления могут поддерживать сценарии маркировки, где часть текстовой метки предоставляет сведения для нетекстовой части или элемент управления предназначен для того, чтобы находиться в известной связи с метками с другим элементом управления в пользовательском интерфейсе. Если можно предоставить полезное поведение на основе классов, можно переопределить GetLabeledByCore , чтобы обеспечить это поведение.

GetBoundingRectangleCore и GetClickablePointCore используются главным образом для сценариев автоматического тестирования. Если вы хотите поддерживать автоматическое тестирование для элемента управления, может потребоваться переопределить эти методы. Это может потребоваться для элементов управления диапазонного типа, где вы не можете предложить только одну точку, так как щелчок пользователя в координатном пространстве оказывает различное воздействие на диапазон. Например, автоматизированный одноранговый объект ScrollBar по умолчанию переопределяет GetClickablePointCore, чтобы вернуть значение точки 'не число'.

GetLiveSettingCore влияет на значение элемента управления LiveSetting по умолчанию для автоматизации пользовательского интерфейса. Это может потребоваться переопределить, если вы хотите, чтобы элемент управления возвращал значение, отличное от AutomationLiveSetting.Off. Дополнительные сведения о том, что представляет LiveSetting , см. в разделе AutomationProperties.LiveSetting.

Вы можете переопределить GetOrientationCore, если у вашего элемента управления есть настраиваемое свойство ориентации, которое может быть сопоставлено с AutomationOrientation. Это делают классы ScrollBarAutomationPeer и SliderAutomationPeer .

Базовая реализация в FrameworkElementAutomationPeer

Базовая реализация FrameworkElementAutomationPeer предоставляет сведения об автоматизации, полученные из макета и состояния поведения на уровне платформы.

  • GetBoundingRectangleCore: возвращает структуру Rect на основе известных характеристик макета. Возвращает 0-значение Rect , если IsOffscreenимеет значение true.
  • GetClickablePointCore: возвращает структуру Point на основе известных характеристик макета, если BoundingRectangle ненулевой.
  • GetNameCore: более обширное поведение, чем можно свести здесь; см. раздел GetNameCore. В основном он пытается преобразовать в строку любое известное содержимое ContentControl или связанных классов с содержимым. Кроме того, если для LabeledBy есть значение, значение имени этого элемента используется в качестве имени.
  • HasKeyboardFocusCore: вычисляется на основе свойств FocusState владельца и IsEnabled . Элементы, которые не являются элементами управления, всегда возвращают значение false.
  • IsEnabledCore: вычисляется на основе свойства IsEnabled владельца, если это элемент управления. Элементы, которые не являются элементами управления, всегда возвращают значение true. Это не означает, что владелец активирован в обычном смысле интеракции; это означает, что одноранговый узел активирован, несмотря на то, что владелец не имеет свойства IsEnabled.
  • IsKeyboardFocusableCore: возвращает true, если владелец - элемент управления; в противном случае - false.
  • IsOffscreenCore: ВидимостьCollapsed у элемента владельца или любого из его родителей приравнивается к true значению IsOffscreen. Исключение: объект всплывающего окна может быть видимым, даже если родители его владельца не видны.
  • SetFocusCore: вызывает Focus.
  • GetParent: Вызывает FrameworkElement.Parent у владельца и находит соответствующий одноранговый узел. Это не переопределение пары с методом Core, поэтому вы не можете изменить это поведение.

Замечание

Одноранговые узлы платформы по умолчанию реализуют поведение с помощью внутреннего машинного кода, а не с помощью проецируемого управляемого кода. Вы не сможете проверить полные сведения о реализации с помощью отражения среды CLR или аналогичных методов. Кроме того, вы не увидите отдельные справочные страницы для переопределений поведения однорангового класса, специфичных для подклассов. Например, у объекта TextBoxAutomationPeer может быть дополнительное поведение для GetNameCore, которое не будет описано на справочной странице AutomationPeer.GetNameCore, и нет отдельной справочной страницы для TextBoxAutomationPeer.GetNameCore. Вместо этого ознакомьтесь со справочным разделом для наиболее близкого однорангового класса и найдите замечания по реализации в разделе "Примечания".

Коллеги и AutomationProperties

Одноранговый узел автоматизации должен предоставлять разумные значения по умолчанию для метаданных специальных возможностей. Вы по-прежнему можете переопределить части этих метаданных с помощью присоединенных свойств AutomationProperties в экземплярах элементов управления. Это относится как к встроенным, так и к пользовательским элементам управления. Например: <Button AutomationProperties.Name="Special" AutomationProperties.HelpText="This is a special button."/>

Дополнительные сведения о присоединенных свойствах AutomationProperties см. в разделе "Основные сведения о специальных возможностях".

Некоторые методы AutomationPeer существуют для удовлетворения контрактов общего поставщика, но редко настраиваются в одноранговых узлах управления, так как приложение обычно предоставляет эти данные через AutomationProperties. Например, многие приложения устанавливают меточные связи с AutomationProperties.LabeledBy. В отличие от этого , LabeledByCore обычно реализуется только в одноранговых узлах, которые моделиируют внутренние связи элементов и заголовков.

Реализация шаблонов

Рассмотрим элемент управления, предоставляющий поведение развертывания и свертывания. Его одноранговый узел должен возвращать себя, когда GetPattern запрашивается для PatternInterface.ExpandCollapse, реализует IExpandCollapseProvider и определяет expand, collapse и ExpandCollapseState.

Включите доступность в сам API управления. Если поведение может быть активировано как непосредственным взаимодействием с пользовательским интерфейсом, так и с помощью шаблона автоматизации, направляйте оба пути через общую логику управления. Например, если обработчики кнопки и команды клавиатуры могут развернуть или свернуть состояние, они должны вызывать те же внутренние методы, которые используются в одноранговых реализациях Expand и Collapse. Это обеспечивает согласованность переходов поведения и визуального состояния независимо от пути вызова.

Типичная реализация заключается в том, что API-интерфейсы поставщика сначала вызывают владельца для доступа к экземпляру элемента управления во время выполнения. Затем можно вызвать необходимые методы поведения для этого объекта.

public class IndexCardAutomationPeer : FrameworkElementAutomationPeer, IExpandCollapseProvider {
    private IndexCard ownerIndexCard;
    public IndexCardAutomationPeer(IndexCard owner) : base(owner)
    {
         ownerIndexCard = owner;
    }
}

Альтернативная реализация заключается в том, что сам элемент управления может ссылаться на свой аналог. Это распространенный шаблон, если вы создаете события автоматизации из элемента управления, так как метод RaiseAutomationEvent является одноранговым методом.

События автоматизации пользовательского интерфейса

События автоматизации пользовательского интерфейса обычно делятся на эти категории.

Event Описание
Изменение свойства Возникает при изменении свойства элемента автоматизации пользовательского интерфейса или шаблона элемента управления. Например, если клиенту требуется отслеживать элемент управления флажка приложения, он может зарегистрироваться для отслеживания события изменения свойства ToggleState. Если флажок установлен или снят, поставщик запускает событие, и клиент может действовать по мере необходимости.
Действие элемента Возникает при изменении пользовательского интерфейса в результате пользовательского или программного действия; например, когда кнопка нажимается или вызывается через шаблон Invoke.
Изменение структуры Возникает при изменении структуры дерева автоматизации пользовательского интерфейса. Структура изменяется, когда новые элементы пользовательского интерфейса становятся видимыми, скрытыми или удаленными на рабочем столе.
Глобальные изменения Срабатывает, когда происходят события, влияющие на пользователя, например, когда фокус ввода перемещается с одного элемента на другой или когда закрывается дочернее окно. Некоторые события не обязательно означают, что состояние пользовательского интерфейса изменилось. Например, если пользователь вкладывается в поле записи текста, а затем нажимает кнопку, чтобы обновить поле, событие TextChanged запускается, даже если пользователь фактически не изменил текст. При обработке события может потребоваться для клиентского приложения проверить, изменилось ли что-либо, прежде чем принимать меры.

Идентификаторы для AutomationEvents

События автоматизации пользовательского интерфейса определяются значениями AutomationEvents . Значения перечисления однозначно определяют тип события.

Инициирование событий

Клиенты автоматизации пользовательского интерфейса могут подписаться на события автоматизации. В одноранговой модели автоматизации одноранговые узлы для пользовательских элементов управления должны сообщать об изменениях состояния управления, которые относятся к специальным возможностям путем вызова метода RaiseAutomationEvent . Аналогично, когда изменяется значение свойства автоматизации пользовательского интерфейса, пользовательские одноранговые элементы управления должны вызывать метод RaisePropertyChangedEvent.

В следующем примере кода показано, как получить одноранговый объект из кода определения элемента управления и вызвать метод для запуска события из этого однорангового узла. В качестве оптимизации код определяет, есть ли прослушиватели для этого типа события. Запуск события и создание однорангового объекта выполняются только в том случае, если есть прослушиватели. Это позволяет избежать ненужных накладных расходов и помогает элементу управления оставаться отзывчивым.

if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
    NumericUpDownAutomationPeer peer =
        FrameworkElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;
    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}

Навигация по одноранговым узлам

После получения однорангового узла клиенты автоматизации пользовательского интерфейса могут навигировать по структуре узлов приложения, вызывая GetChildren и GetParent. В элементе управления дочерняя навигация создается с помощью GetChildrenCore, которая используется UI Automation для создания поддерева (например, элементов списка). Реализация по умолчанию в FrameworkElementAutomationPeer проходит по визуальному дереву. Пользовательские элементы управления могут переопределить это, чтобы предоставить более содержательное представление автоматизации.

Собственная поддержка автоматизации текстовых шаблонов

Некоторые одноранговые узлы платформы по умолчанию поддерживают текстовые шаблоны (PatternInterface.Text) с помощью собственных сведений о реализации, поэтому ITextProvider может не отображаться в управляемом наследовании. Тем не менее, запросы шаблона от управляемых или неуправляемых клиентов по-прежнему могут сообщать о поддержке текстовых образцов и демонстрировать соответствующее поведение.

Если вы планируете наследоваться от одного из элементов управления текстом, а также создать пользовательский одноранговый узел, производный от одного из текстовых одноранговых узлов, проверьте разделы "Примечания" в справочнике по API, чтобы узнать больше о поддержке шаблонов на уровне встроенных средств. Вы можете получить доступ к встроенному базовому поведению в настраиваемом одноранговом узле, если вызвать базовую реализацию из реализаций управляемого интерфейса поставщика, но трудно изменить то, что делает базовая реализация, так как собственные интерфейсы не открыты как в одноранговом узле, так и в его управляющем контроле. Как правило, следует использовать базовые реализации как есть (вызов только базы) или полностью заменить функциональность собственным управляемым кодом и не вызывать базовую реализацию. Последний — это расширенный сценарий, и требуется строгое знакомство с платформой текстовых служб, используемой вашим элементом управления для удовлетворения требований специальных возможностей.

AutomationProperties.AccessibilityView

Помимо пользовательских узлов, можно настроить представление дерева автоматизации для каждого экземпляра элемента управления, задав AutomationProperties.AccessibilityView в XAML. Это не код, разрабатываемый в сотрудничестве с коллегами, но это часто важно при улучшении доступности для пользовательских шаблонов.

Основной вариант использования — опущение частей шаблона, которые не вносят значимого вклада в семантику доступности для составного управления. Для этого задайте параметру AutomationProperties.AccessibilityView значение Raw.

Создание исключений из одноранговых узлов автоматизации

Одноранговые реализации могут вызывать исключения, и ожидается, что надежные клиенты будут продолжать обработку. На практике клиенты часто проверяют деревья автоматизации, охватывающие несколько приложений, поэтому сбой в одном поддереве не должен завершать процесс клиента.

Проверка входных данных корректна. Например, создайте исключение ArgumentNullException , если значение NULL недопустимо для реализации. Кроме того, учет времени: одноранговые взаимодействия не всегда синхронизированы с текущим визуальным состоянием, поэтому состояние управления может измениться между созданием однорангового объекта и выполнением метода. В этих сценариях поставщики могут использовать два выделенных исключения:

  • Возбросьте исключение ElementNotAvailableException, если вы не можете получить доступ ни к владельцу элемента-аналога, ни к связанному элементу-аналогу на основе исходной информации, переданной API. Например, у вас может быть одноранговый элемент, который пытается выполнить свои методы, но владелец уже был удален из пользовательского интерфейса, например закрытое модальное окно. Для клиента, работающего вне платформы .NET, это сопоставляется с UIA_E_ELEMENTNOTAVAILABLE.
  • Вызов ElementNotEnabledException, если по-прежнему существует владелец, но этот владелец находится в режиме, таком как IsEnabled=false, который блокирует некоторые определенные программные изменения, которые ваш партнёр пытается выполнить. Для клиента, не использующего .NET, это соответствует UIA_E_ELEMENTNOTENABLED.

В целом, быть консервативным с исключениями. Многие клиенты не могут преобразовать исключения поставщика в значимые действия пользователей. В некоторых случаях поведение ничего не делающего или локальная обработка исключений предпочтительнее, чем многократный выброс исключений. Кроме того, помните, что многие клиенты автоматизации пользовательского интерфейса основаны на COM и в первую очередь оценивают результаты HRESULT , такие как S_OK.