Реализация поставщика Server-Side модель автоматизации пользовательского интерфейса

В этом разделе описывается, как реализовать серверный поставщик microsoft модель автоматизации пользовательского интерфейса для пользовательского элемента управления, написанного на C++. Он содержит следующие подразделы:

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

Структура дерева поставщика

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

Например, каждый элемент должен реализовывать IRawElementProviderFragment , а корневой элемент приложения должен реализовывать IRawElementProviderFragmentRoot. Кроме того, каждый элемент поставщика должен ссылаться на:

  • родитель
  • элемент prior provider
  • следующий элемент provider
  • первый дочерний поставщик
  • последний дочерний элемент поставщика

Интерфейсы поставщика

Следующие интерфейсы COM предоставляют функциональные возможности для пользовательских элементов управления. Чтобы обеспечить основные функциональные возможности, каждый поставщик модель автоматизации пользовательского интерфейса должен реализовать по крайней мере интерфейс IRawElementProviderSimple. Интерфейсы IRawElementProviderFragment и IRawElementProviderFragmentRoot являются необязательными, но должны быть реализованы для элементов в сложном элементе управления для предоставления дополнительных функциональных возможностей.

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

 

Примечание

В API модель автоматизации пользовательского интерфейса для управляемого кода эти интерфейсы образуют иерархию наследования. Это не так в C++, где интерфейсы полностью разделены.

 

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

Интерфейс Описание
IRawElementProviderAdviseEvents Позволяет поставщику отслеживать запросы событий.
IRawElementProviderHwndOverride Позволяет изменять положение оконных элементов в дереве модель автоматизации пользовательского интерфейса фрагмента.

 

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

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

Функциональность Реализация
Предоставление поставщику модель автоматизации пользовательского интерфейса. В ответ на сообщение WM_GETOBJECT , отправленное в окно управления, верните объект, реализующий IRawElementProviderSimple. Для фрагментов это должен быть поставщик для корневого фрагмента.
Укажите значения свойств. Реализуйте IRawElementProviderSimple::GetPropertyValue для предоставления или переопределения значений.
Включите взаимодействие клиента с элементом управления . Реализуйте интерфейсы, поддерживающие каждый соответствующий шаблон элемента управления, например IInvokeProvider. Верните эти поставщики шаблонов элементов управления в реализации IRawElementProviderSimple::GetPatternProvider.
Вызов событий. UiaRaiseAutomationEvent, методы IProxyProviderWinEventSink.
Включите навигацию и фокусировку во фрагменте. Реализуйте IRawElementProviderFragment для каждого элемента в фрагменте. Не требуется для элементов, которые не являются частью фрагмента.
Включение фокусировки и поиска дочерних элементов во фрагменте. Реализуйте IRawElementProviderFragmentRoot. Не требуется для элементов, которые не являются корнями фрагментов.

 

Значения свойств

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

Как правило, поставщикам оконных элементов управления не требуется предоставлять следующие свойства, идентифицируемые с помощью PROPERTYID:

Свойство 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. Корень фрагмента не поддерживает переход к родительскому элементу или к одноуровневым элементам; этот параметр обрабатывается поставщиком окна узла.

Элементы фрагмента, отличные от корневого, должны поддерживать переходы к родительскому элементу, а также любым одноуровневым и дочерним элементам.

Назначение нового родителя

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

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

  1. Создайте поставщик для всплывающего окна. Для этого необходимо, чтобы класс всплывающего окна был известен заранее.
  2. Реализуйте все свойства и шаблоны элементов управления, как обычно для этого всплывающего окна, как если бы это был собственный элемент управления.
  3. Реализуйте свойство IRawElementProviderSimple::HostRawElementProvider , чтобы оно возвращало значение, полученное из UiaHostProviderFromHwnd, где параметр является дескриптором окна всплывающего окна.
  4. Реализуйте IRawElementProviderFragment::Navigate для всплывающего окна и его родительского элемента, чтобы навигация правильно обрабатывалась от логического родительского элемента к логическим дочерним элементам и между дочерними элементами одного и того же уровня.

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

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

Изменение положения поставщика

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

Хорошим примером служит элемент управления главной панели. Элемент управления rebar содержит полосы, каждый из которых, в свою очередь, может содержать элемент управления на основе окна, например панель инструментов, поле редактирования или поле со списком. Поставщик окон по умолчанию для окна перекладки видит окна управления полосой как дочерние, а поставщик панели — как дочерние. Так как поставщик окон и поставщик перекладок работают в тандеме и объединяют свои дочерние элементы, полосы и оконные элементы управления отображаются как дочерние элементы управления панели. Однако логически только полосы должны отображаться в качестве дочерних элементов элемента управления rebar, и каждый поставщик диапазона должен быть связан с поставщиком окон по умолчанию для элемента управления, который он содержит.

Для этого корневой поставщик фрагментов для элемента управления rebar предоставляет набор дочерних элементов, представляющих полосы. Каждый диапазон имеет одного поставщика, который может предоставлять свойства и шаблоны элементов управления. В реализации IRawElementProviderSimple::HostRawElementProvider поставщик диапазона возвращает поставщик окон по умолчанию для окна элемента управления, который он получает путем вызова UiaHostProviderFromHwnd, передавая дескриптор окна элемента управления (HWND). Наконец, корневой поставщик фрагментов для панели rebar реализует интерфейс IRawElementProviderHwndOverride , а в его реализации IRawElementProviderHwndOverride::GetOverrideProviderForHwnd возвращает соответствующий поставщик диапазона для элемента управления, содержащегося в указанном окне.

Отключение поставщиков

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

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

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