Partager via


Implémenter un fournisseur de Server-Side UI Automation

Cette rubrique explique comment implémenter un fournisseur Microsoft UI Automation côté serveur pour un contrôle personnalisé écrit en C++. Il contient les sections suivantes :

Pour obtenir des exemples de code qui montrent comment implémenter des fournisseurs côté serveur, consultez Rubriques de procédure pour les fournisseurs UI Automation.

Structure de l’arborescence du fournisseur

Vous devez implémenter un fournisseur UIA pour chaque élément d’interface utilisateur qui doit être accessible à un client UIA.

Par exemple, chaque élément doit implémenter IRawElementProviderFragment tandis que l’élément racine de l’application doit implémenter IRawElementProviderFragmentRoot. En outre, chaque élément fournisseur doit être lié à un :

  • parent
  • élément fournisseur antérieur
  • élément fournisseur suivant
  • enfant du premier fournisseur
  • dernier enfant du fournisseur

Interfaces de fournisseur

Les interfaces COM (Component Object Model) suivantes fournissent des fonctionnalités pour les contrôles personnalisés. Pour fournir des fonctionnalités de base, chaque fournisseur UI Automation doit implémenter au moins l’interface IRawElementProviderSimple. Les interfaces IRawElementProviderFragment et IRawElementProviderFragmentRoot sont facultatives, mais doivent être implémentées pour les éléments d’un contrôle complexe afin de fournir des fonctionnalités supplémentaires.

Interface Description
IRawElementProviderSimple Fournit des fonctionnalités de base pour un contrôle hébergé dans une fenêtre, notamment la prise en charge des modèles de contrôle et des propriétés.
IRawElementProviderFragment Ajoute des fonctionnalités pour un élément dans un contrôle complexe, notamment la navigation dans le fragment, la définition du focus et le retour du rectangle englobant de l’élément.
IRawElementProviderFragmentRoot Ajoute des fonctionnalités pour l'élément racine d'un contrôle complexe, y compris la localisation d'un élément enfant à des coordonnées spécifiées et la définition de l'état du focus pour l'ensemble du contrôle.

 

Notes

Dans l’API UI Automation pour le code managé, ces interfaces forment une hiérarchie d’héritage. Ce n’est pas le cas en C++, où les interfaces sont complètement séparées.

 

Les interfaces suivantes fournissent des fonctionnalités supplémentaires, mais l’implémentation est facultative.

Interface Description
IRawElementProviderAdviseEvents Permet au fournisseur de suivre des demandes pour des événements.
IRawElementProviderHwndOverride Permet le repositionnement d’éléments basés sur une fenêtre dans l’arborescence UI Automation d’un fragment.

 

Fonctionnalités requises pour les fournisseurs UI Automation

Pour communiquer avec UI Automation, votre contrôle doit implémenter les main zones de fonctionnalités décrites dans le tableau suivant.

Fonctionnalités Implémentation
Exposez le fournisseur à UI Automation. En réponse à un message WM_GETOBJECT envoyé à la fenêtre de contrôle, retournez l’objet qui implémente IRawElementProviderSimple. Pour les fragments, il doit s’agir du fournisseur pour la racine du fragment.
Fournissez des valeurs de propriété. Implémentez IRawElementProviderSimple::GetPropertyValue pour fournir ou remplacer des valeurs.
Permettre au client d’interagir avec le contrôle. Implémentez des interfaces qui prennent en charge chaque modèle de contrôle approprié, comme IInvokeProvider. Retournez ces fournisseurs de modèles de contrôle dans votre implémentation de IRawElementProviderSimple::GetPatternProvider.
Déclencher des événements. UiaRaiseAutomationEvent, méthodes de IProxyProviderWinEventSink.
Activez la navigation et la mise au point dans un fragment. Implémentez IRawElementProviderFragment pour chaque élément du fragment. Non nécessaire pour les éléments qui ne font pas partie d’un fragment.
Activez la mise au point et la localisation des éléments enfants dans un fragment. Implémentez IRawElementProviderFragmentRoot. Non nécessaire pour les éléments qui ne sont pas des racines fragmentaires.

 

Valeurs de la propriété

UI Automation fournisseurs pour les contrôles personnalisés doivent prendre en charge certaines propriétés qui peuvent être utilisées par UI Automation et par les applications clientes. Pour les éléments hébergés dans windows, UI Automation peut récupérer certaines propriétés du fournisseur de fenêtres par défaut, mais doit en obtenir d’autres auprès du fournisseur personnalisé.

En règle générale, les fournisseurs de contrôles basés sur une fenêtre n’ont pas besoin de fournir les propriétés suivantes identifiées par PROPERTYID :

La propriété RuntimeId d’un élément simple ou d’un fragment racine hébergé dans une fenêtre est obtenue à partir de la fenêtre. Toutefois, les éléments fragments sous la racine, tels que les éléments de liste dans une zone de liste, doivent fournir leurs propres identificateurs. Pour plus d’informations, consultez IRawElementProviderFragment::GetRuntimeId.

La propriété IsKeyboardFocusable doit être retournée pour les fournisseurs hébergés dans un contrôle Windows Forms. Dans ce cas, le fournisseur de fenêtre par défaut ne pourra peut-être pas récupérer la bonne valeur.

La propriété Name est généralement fournie par le fournisseur hôte.

Événements des fournisseurs

Les fournisseurs UI Automation doivent déclencher des événements pour informer les applications clientes des modifications de l’état de l’interface utilisateur. Les fonctions suivantes sont utilisées pour déclencher des événements.

Fonction Description
UiaRaiseAutomationEvent Déclenche différents événements, y compris les événements déclenchés par les modèles de contrôle.
UiaRaiseAutomationPropertyChangedEvent Déclenche un événement quand une propriété UI Automation a été modifiée.
UiaRaiseStructureChangedEvent Déclenche un événement lorsque la structure de l’arborescence UI Automation a changé, par exemple en supprimant ou en ajoutant un élément.

 

L’objectif d’un événement est d’informer le client d’un événement qui se produit dans l’interface utilisateur. Les fournisseurs doivent déclencher un événement, que la modification ait été déclenchée par une entrée utilisateur ou par une application cliente à l’aide de UI Automation. Par exemple, l’événement identifié par UIA_Invoke_InvokedEventId doit être déclenché chaque fois que le contrôle est appelé, soit via une entrée utilisateur directe, soit par l’application cliente appelant IUIAutomationInvokePattern::Invoke.

Pour optimiser les performances, un fournisseur peut déclencher des événements de manière sélective ou ne pas en déclencher du tout si aucune application cliente n'est enregistrée pour les recevoir. Les éléments d’API suivants sont utilisés pour l’optimisation.

Élément API Description
UiaClientsAreListening Cette fonction détermine si des applications clientes se sont abonnées à des événements UI Automation.
IRawElementProviderAdviseEvents L’implémentation de cette interface sur une racine de fragment permet au fournisseur d’être informé lorsque les clients inscrivent et annulent l’inscription des gestionnaires d’événements pour les événements sur le fragment.

 

Notes

À l’instar de l’implémentation du comptage de références dans la programmation COM, il est important pour les fournisseurs UI Automation de traiter les méthodes IRawElementProviderAdviseEvents::AdviseEventAdded et AdviseEventRemoved telles que les méthodes IUnknown::AddRef et Release de l’interface IUnknown. Tant qu’AdviseEventAdded a été appelé plus de fois qu’AdviseEventRemoved pour un événement ou une propriété spécifique, le fournisseur doit continuer à déclencher les événements correspondants, car certains clients sont toujours à l’écoute. Les fournisseurs UI Automation peuvent également utiliser la fonction UiaClientsAreListening pour déterminer si au moins un client écoute et, si c’est le cas, déclencher tous les événements appropriés.

 

Navigation du fournisseur

Les fournisseurs de contrôles simples, tels qu’un bouton personnalisé hébergé dans une fenêtre, n’ont pas besoin de prendre en charge la navigation dans l’arborescence UI Automation. La navigation vers et depuis l’élément est gérée par le fournisseur par défaut de la fenêtre hôte, qui est spécifié dans l’implémentation de IRawElementProviderSimple::HostRawElementProvider. Quand vous implémentez un fournisseur pour un contrôle personnalisé complexe, vous devez toutefois prendre en charge la navigation entre le nœud racine du fragment et ses descendants, et entre nœuds frères.

Notes

Les éléments d’un fragment autre que la racine doivent retourner NULL à partir de HostRawElementProvider, car ils ne sont pas hébergés directement dans une fenêtre et aucun fournisseur par défaut ne peut prendre en charge la navigation vers et depuis ces éléments.

 

La structure du fragment est déterminée par votre implémentation de IRawElementProviderFragment::Navigate. Pour chaque direction possible à partir de chaque fragment, cette méthode retourne l'objet fournisseur de l'élément dans cette direction. S’il n’y a aucun élément dans cette direction, la méthode retourne NULL.

La racine du fragment ne prend en charge la navigation que vers les éléments enfants. Par exemple, une zone de liste retourne le premier élément de la liste lorsque la direction est NavigateDirection_FirstChild, et retourne le dernier élément lorsque la direction est NavigateDirection_LastChild. La racine du fragment ne prend pas en charge la navigation vers un parent ou des frères et sœurs ; cette opération est gérée par le fournisseur de fenêtres de l’hôte.

Les éléments d'un fragment qui ne sont pas la racine doivent prendre en charge la navigation vers le parent et tous leurs frères et enfants.

Affectation d’un nouveau parent

Les fenêtres contextuelles sont en fait des fenêtres de niveau supérieur et, par défaut, apparaissent dans l’arborescence UI Automation en tant qu’enfants du bureau. Dans de nombreux cas, toutefois, les fenêtres indépendantes sont logiquement des enfants d'autres contrôles. Par exemple, la liste déroulante d'une zone de liste modifiable est logiquement un enfant de la zone de liste modifiable. De la même façon, une fenêtre indépendante de menu est logiquement un enfant du menu. UI Automation prend en charge l’affectation d’un nouveau parent à une fenêtre contextuelle afin qu’il semble être un enfant du contrôle associé.

Pour affecter un nouveau parent à une fenêtre contextuelle :

  1. Créez un fournisseur pour la fenêtre indépendante. Cela nécessite que la classe de la fenêtre contextuelle soit connue à l’avance.
  2. Implémentez toutes les propriétés et les modèles de contrôle comme d’habitude pour cette fenêtre contextuelle, comme s’il s’agissait d’un contrôle en soi.
  3. Implémentez la propriété IRawElementProviderSimple::HostRawElementProvider afin qu’elle retourne la valeur obtenue à partir de UiaHostProviderFromHwnd, où le paramètre est le handle de fenêtre de la fenêtre contextuelle.
  4. Implémentez IRawElementProviderFragment::Navigate pour la fenêtre contextuelle et son parent afin que la navigation soit gérée correctement du parent logique aux enfants logiques et entre les enfants frères.

Quand UI Automation rencontre la fenêtre contextuelle, il reconnaît que cette navigation est substituée à la valeur par défaut et il ignore la fenêtre contextuelle quand elle est rencontrée en tant qu’enfant du bureau. Au lieu de cela, le nœud n’est accessible que par le biais du fragment.

L’affectation d’un nouveau parent ne convient pas aux cas où un contrôle peut héberger une fenêtre de n’importe quelle classe. Par exemple, un contrôle de barre d’outils peut héberger n’importe quel type de fenêtre dans ses bandes. Pour gérer ces cas, UI Automation prend en charge une autre forme de déplacement de fenêtre, comme décrit dans la section suivante.

Repositionnement du fournisseur

UI Automation fragments peuvent contenir au moins deux éléments contenus dans une fenêtre. Étant donné que chaque fenêtre a son propre fournisseur par défaut qui considère la fenêtre comme un enfant d’une fenêtre contenante, l’arborescence UI Automation par défaut affiche les fenêtres dans le fragment en tant qu’enfants de la fenêtre parente. Dans la plupart des cas, ce comportement est conseillé, mais il peut parfois porter à confusion, car il ne correspond pas à la structure logique de l’interface utilisateur.

Le contrôle rebar en est un bon exemple. Un contrôle de barre d’outils contient des bandes, chacune d’elles pouvant à son tour contenir un contrôle basé sur une fenêtre, comme une barre d’outils, une zone d’édition ou une zone de liste déroulante. Le fournisseur de fenêtres par défaut de la fenêtre rebar voit les fenêtres de contrôle de bande comme des enfants, et le fournisseur de barres d’barres d’outils voit les bandes en tant qu’enfants. Étant donné que le fournisseur de fenêtres et le fournisseur de barres d’barres fonctionnent en tandem et combinent leurs enfants, les bandes et les contrôles basés sur la fenêtre apparaissent en tant qu’enfants du contrôle de barre d’barres. Toutefois, en toute logique, seules les bandes doivent apparaître en tant qu’enfants du contrôle de barre d’barres, et chaque fournisseur de bandes doit être associé au fournisseur de fenêtres par défaut pour le contrôle qu’il contient.

Pour ce faire, le fournisseur racine de fragments pour le contrôle de barre d’outils expose un ensemble d’enfants représentant les bandes. Chaque bande a un fournisseur unique qui peut exposer des propriétés et des modèles de contrôle. Dans son implémentation de IRawElementProviderSimple::HostRawElementProvider, le fournisseur de bandes retourne le fournisseur de fenêtres par défaut pour la fenêtre de contrôle, qu’il obtient en appelant UiaHostProviderFromHwnd, en passant le handle de fenêtre du contrôle (HWND). Enfin, le fournisseur racine de fragments de la barre d’outils implémente l’interface IRawElementProviderHwndOverride et, dans son implémentation de IRawElementProviderHwndOverride::GetOverrideProviderForHwnd, il retourne le fournisseur de bandes approprié pour le contrôle contenu dans la fenêtre spécifiée.

Déconnexion des fournisseurs

Les applications créent généralement des contrôles à mesure qu’ils sont nécessaires et les détruisent par la suite. Après avoir détruit un contrôle, les ressources du fournisseur de UI Automation associées au contrôle doivent être libérées en appelant uiaDisconnectProvider.

De même, une application doit utiliser la fonction UiaDisconnectAllProviders pour libérer toutes les ressources UI Automation détenues par tous les fournisseurs de l’application avant de s’arrêter.

Guide du programmeur du fournisseur UI Automation