Реализация поставщика автоматизации пользовательского интерфейса Server-Side
В этом разделе описывается реализация поставщика автоматизации пользовательского интерфейса Майкрософт на стороне сервера для пользовательского элемента управления, написанного на языке C++. Он содержит следующие разделы:
- интерфейсы поставщика
- необходимые функциональные возможности для поставщиков автоматизации пользовательского интерфейса
- значения свойств
- События от провайдеров
- навигация поставщика
- назначение нового родительского
- Перемещение поставщика
- Отключение поставщиков
- связанные темы
Примеры кода, демонстрирующие реализацию поставщиков на стороне сервера, см. в разделах How-To для поставщиков автоматизации пользовательского интерфейса.
Структура дерева поставщика
Необходимо реализовать поставщик UIA для каждого элемента пользовательского интерфейса, который должен быть доступен клиенту UIA.
Например, каждый элемент должен реализовать IRawElementProviderFragment в то время как корневой элемент приложения должен реализовать IRawElementProviderFragmentRoot. Кроме того, каждый элемент поставщика должен ссылаться на:
- родитель
- предыдущий элемент поставщика услуг
- следующий элемент поставщика
- первый дочерний поставщик
- последний дочерний поставщик
Интерфейсы поставщика
Следующие интерфейсы объектной модели компонента (COM) предоставляют функциональные возможности для пользовательских элементов управления. Чтобы обеспечить основные функциональные возможности, каждый поставщик автоматизации пользовательского интерфейса должен реализовать по крайней мере интерфейс IRawElementProviderSimple. Интерфейсы IRawElementProviderFragment и IRawElementProviderFragmentRoot являются необязательными, но их следует реализовать для элементов сложного элемента управления, чтобы обеспечить дополнительные функциональные возможности.
Интерфейс | Описание |
---|---|
IRawElementProviderSimple | Предоставляет основные функции для элемента управления, размещенного в окне, включая поддержку шаблонов элементов управления и свойств. |
IRawElementProviderFragment | Добавляет функции для элемента в сложном элементе управления, включая навигацию в фрагменте, задание фокуса и возвращение ограничивающего прямоугольника элемента. |
IRawElementProviderFragmentRoot | Добавляет функциональные возможности для корневого элемента в сложном элементе управления, включая поиск дочернего элемента по указанным координатам и настройку состояния фокуса для всего элемента управления. |
Заметка
В API автоматизации пользовательского интерфейса для управляемого кода эти интерфейсы образуют иерархию наследования. Это не так в C++, где интерфейсы полностью отделены.
Следующие интерфейсы предоставляют добавленные функциональные возможности, но реализация является необязательной.
Интерфейс | Описание |
---|---|
IRawElementProviderAdviseEvents | Позволяет поставщику отслеживать запросы на события. |
IRawElementProviderHwndOverride | Позволяет изменить расположение элементов, основанных на окнах, в дереве автоматизации пользовательского интерфейса фрагмента. |
Обязательные функции для поставщиков автоматизации пользовательского интерфейса
Чтобы взаимодействовать с автоматизацией пользовательского интерфейса, элемент управления должен реализовать основные области функциональных возможностей, описанные в следующей таблице.
Функциональность | Реализация |
---|---|
Предоставление поставщику возможности автоматизации пользовательского интерфейса. | В ответ на сообщение WM_GETOBJECT, отправленное в окно управления, верните объект, реализующий IRawElementProviderSimple. Для фрагментов это должен быть поставщик корневой части фрагмента. |
Укажите значения свойств. | Реализуйте IRawElementProviderSimple::GetPropertyValue для предоставления или переопределения значений. |
Разрешить клиенту взаимодействовать с элементом управления. | Реализуйте интерфейсы, поддерживающие каждый соответствующий шаблон элемента управления, например IInvokeProvider. Возвратите этих поставщиков шаблонов управления в вашей реализации IRawElementProviderSimple::GetPatternProvider. |
Инициация событий. | UiaRaiseAutomationEvent, методы IProxyProviderWinEventSink. |
Включите навигацию и фокусирование в фрагменте. | Реализуйте IRawElementProviderFragment для каждого элемента в фрагменте. Не требуется для элементов, которые не являются частью фрагмента. |
Включите фокусирование и поиск дочерних элементов в фрагменте. | Реализуйте IRawElementProviderFragmentRoot. Не требуется для элементов, которые не являются корнями фрагментов. |
Значения свойств
Поставщики автоматизации пользовательского интерфейса для пользовательских элементов управления должны поддерживать определенные свойства, которые могут использоваться службой автоматизации пользовательского интерфейса и клиентскими приложениями. Для элементов, размещенных в Windows, служба автоматизации интерфейса пользователя может получить некоторые свойства от стандартного поставщика окон, но должна получить другие от пользовательского поставщика.
Как правило, поставщикам элементов управления на основе окон не нужно предоставлять следующие свойства, которые определяются PROPERTYID:
- UIA_BoundingRectanglePropertyId
- UIA_ClickablePointPropertyId
- UIA_ProcessIdPropertyId
- UIA_ClassNamePropertyId
- UIA_HasKeyboardFocusPropertyId
- UIA_IsEnabledPropertyId
- UIA_IsKeyboardFocusablePropertyId
- UIA_IsPasswordPropertyId
- UIA_NamePropertyId
- UIA_RuntimeIdPropertyId
Свойство RuntimeId простого элемента или корневого фрагмента, размещенного в окне, получается из окна. Однако элементы фрагмента под корнем, такие как элементы списка в поле списка, должны предоставлять собственные идентификаторы. Дополнительные сведения см. в разделе IRawElementProviderFragment::GetRuntimeId.
Свойство IsKeyboardFocusable должно быть возвращено для поставщиков, размещенных в элементе управления Windows Forms. В этом случае поставщик окон по умолчанию может не получить правильное значение.
Свойство Name обычно предоставляется поставщиком хостинга.
События от поставщиков
Поставщики службы автоматизации пользовательского интерфейса должны вызывать события, чтобы уведомить клиентские приложения об изменениях в состоянии пользовательского интерфейса. Для вызова событий используются следующие функции.
Функция | Описание |
---|---|
UiaRaiseAutomationEvent | Вызывает различные события, включая события, инициируемые шаблонами элементов управления. |
UiaRaiseAutomationPropertyChangedEvent | Вызывает событие при изменении свойства автоматизации пользовательского интерфейса. |
UiaRaiseStructureChangedEvent | Вызывает событие, когда структура дерева автоматизации пользовательского интерфейса изменилась, например путем удаления или добавления элемента. |
Цель события — уведомить клиента о том, что происходит в пользовательском интерфейсе. Поставщики должны вызывать событие независимо от того, было ли изменение активировано входными данными пользователя или клиентским приложением с помощью автоматизации пользовательского интерфейса. Например, событие, идентифицируемое UIA_Invoke_InvokedEventId, должно вызываться всегда, когда элемент управления вызывается либо через прямой ввод пользователя, либо клиентским приложением, вызывающим IUIAutomationInvokePattern::Invoke.
Для оптимизации производительности поставщик может выборочно вызывать события или не вызывать никаких событий, если для их получения не зарегистрировано клиентское приложение. Для оптимизации используются следующие элементы API.
Элемент API | Описание |
---|---|
UiaClientsAreListening | Эта функция определяет, подписаны ли клиентские приложения на события автоматизации пользовательского интерфейса. |
IRawElementProviderAdviseEvents | Реализация этого интерфейса на корневом элементе фрагмента позволяет поставщику уведомляться, когда клиенты регистрируют и отменяют регистрацию обработчиков событий для событий в фрагменте. |
Заметка
Аналогично реализации подсчета ссылок в программировании COM, важно, чтобы поставщики службы автоматизации пользовательского интерфейса обрабатывали методы IRawElementProviderAdviseEvents::AdviseEventAdded и AdviseEventRemoved так же, как методы IUnknown::AddRef и Release интерфейса IUnknown. До тех пор, пока AdviseEventAdded была вызвана больше раз, чем AdviseEventRemoved для определенного события или свойства, поставщик должен продолжать вызывать соответствующие события, так как некоторые клиенты по-прежнему прослушивают. Кроме того, поставщики автоматизации пользовательского интерфейса могут использовать функцию UiaClientsAreListening, чтобы определить, прослушивает ли по крайней мере один клиент и, если да, вызывает все соответствующие события.
Навигация по услугам провайдера
Поставщики простых элементов управления, например пользовательская кнопка, размещенная в окне, не должны поддерживать навигацию в дереве автоматизации пользовательского интерфейса. Навигация к элементу и от него обрабатывается поставщиком по умолчанию для узлового окна, который указан в реализации IRawElementProviderSimple::HostRawElementProvider. Однако при реализации поставщика для сложного пользовательского элемента управления необходимо поддерживать навигацию между корневым узлом фрагмента и его потомками, а также между узлами-братьями.
Заметка
Элементы фрагмента, отличного от корневого, должны возвращать NULL через HostRawElementProvider, поскольку они не размещены непосредственно в окне, и ни один поставщик по умолчанию не может поддерживать навигацию к ним или от них.
Структура фрагмента определяется вашей реализацией IRawElementProviderFragment::Navigate. Для каждого возможного направления от каждого фрагмента этот метод возвращает объект поставщика, соответствующий элементу в этом направлении. Если в этом направлении нет элемента, метод возвращает NULL.
Корневой элемент фрагмента поддерживает навигацию только к дочерним элементам. Например, поле списка возвращает первый элемент в списке, когда направление NavigateDirection_FirstChild, и возвращает последний элемент, когда направление NavigateDirection_LastChild. Корень фрагмента не поддерживает переход к родительскому элементу или к соседям; это обрабатывается поставщиком оконного узла.
Элементы фрагмента, который не является корнем, должны поддерживать переход к родительскому элементу, а также к любым элементам и дочерним элементам.
Назначение нового родителя
Всплывающие окна на самом деле являются окнами верхнего уровня, и по умолчанию отображаются в дереве автоматизации пользовательского интерфейса в качестве дочерних элементов рабочего стола. Однако во многих случаях всплывающие окна логически являются дочерними элементами другого элемента управления. Например, раскрывающийся список логически является дочерним элементом комбинированного поля. Аналогичным образом всплывающее окно меню логически является дочерним элементом меню. Автоматизация пользовательского интерфейса поддерживает назначение всплывающему окну нового родительского элемента, чтобы оно выглядело как дочерний элемент связанного элемента управления.
Чтобы назначить нового родителя для всплывающего окна, выполните следующее:
- Создайте поставщика для всплывающего окна. Для этого необходимо заранее знать класс всплывающего окна.
- Реализуйте все свойства и шаблоны элемента управления, как для обычного всплывающего окна, как будто это был самостоятельный элемент управления.
- Реализуйте свойство IRawElementProviderSimple::HostRawElementProvider, чтобы оно возвращало значение, полученное из UiaHostProviderFromHwnd, где параметр является дескриптором окна всплывающего окна.
- Реализуйте IRawElementProviderFragment::Navigate во всплывающем окне и его родительском окне, чтобы навигация правильно обрабатывалась от логического родительского элемента к логическим дочерним элементам и между дочерними элементами с братом.
Когда служба автоматизации пользовательского интерфейса встречает всплывающее окно, она распознает, что навигация переопределяется настройками по умолчанию, и пропускает всплывающее окно, когда оно обнаруживается как дочерний элемент рабочего стола. Вместо этого узел доступен только через фрагмент.
Назначение нового родителя не подходит для случаев, когда элемент управления способен размещать окно любого класса. Например, элемент управления rebar может размещать любой тип окна в своих полосах. Для обработки этих случаев автоматизация пользовательского интерфейса поддерживает альтернативную форму перемещения окна, как описано в следующем разделе.
Изменение положения поставщика
Фрагменты автоматизации пользовательского интерфейса могут содержать два или более элементов, содержащихся в окне. Так как каждое окно имеет собственный поставщик по умолчанию, который считает окно дочерним элементом содержащего окна, дерево автоматизации пользовательского интерфейса по умолчанию будет отображать окна в фрагменте как дочерние элементы родительского окна. В большинстве случаев это желательное поведение, но иногда это может привести к путанице, так как она не соответствует логической структуре пользовательского интерфейса.
Хорошим примером этого является контроль арматуры. Элемент управления типа "rebar" содержит ленты, каждая из которых может содержать оконный элемент управления, такой как панель инструментов, поле ввода или комбинированное поле. Поставщик окон по умолчанию для окна перекладины воспринимает окна управления полосами в качестве дочерних элементов, а поставщик перекладины воспринимает полосы в качестве дочерних элементов. Так как поставщик окон и поставщик реек работают в тандеме и объединяют свои дочерние элементы, и полосы, и элементы управления на основе окон отображаются как дочерние элементы управления реечным управлением. Однако только полосы должны отображаться как дочерние элементы элемента управления rebar, и каждый поставщик полос должен быть связан с поставщиком окон по умолчанию для элемента управления, который он содержит.
Для этого корневой поставщик фрагмента для элемента управления rebar предоставляет набор дочерних элементов, представляющих полосы. У каждого диапазона есть один поставщик, который может предоставлять свойства и шаблоны управления. В своей реализации IRawElementProviderSimple::HostRawElementProviderпоставщик полосы возвращает поставщик окна по умолчанию для окна управления, который он получает путем вызова UiaHostProviderFromHwnd, передавая в качестве параметра дескриптор окна управления (HWND). Наконец, корневой поставщик фрагмента для панели реализует интерфейс IRawElementProviderHwndOverride и в реализации IRawElementProviderHwndOverride::GetOverrideProviderForHwnd, он возвращает соответствующий поставщик полосы для элемента управления, содержащегося в указанном окне.
Отключение поставщиков
Приложения обычно создают элементы управления по мере их необходимости и уничтожают их после этого. После уничтожения элемента управления ресурсы поставщика автоматизации пользовательского интерфейса, связанные с элементом управления, должны быть освобождены путем вызова UiaDisconnectProvider.
Аналогичным образом приложение должно использовать функцию UiaDisconnectAllProviders, чтобы освободить все ресурсы автоматизации пользовательского интерфейса, удерживаемые всеми поставщиками в приложении, прежде чем завершить работу.
Связанные разделы