UI Automation d'un contrôle personnalisé WPF

Microsoft UI Automation fournit une interface unique et généralisée que les clients Automation peuvent utiliser pour examiner ou faire fonctionner les interfaces utilisateur de diverses plateformes et infrastructures. L'UI Automation permet à la fois au code d'assurance qualité (test) et aux applications d'accessibilité telles que les lecteurs d'écran d'examiner des éléments de l'interface utilisateur et de simuler l'interaction de l'utilisateur avec ces éléments à partir d'autres codes. Pour plus d'informations sur la prise en charge de l'UI Automation entre toutes les plateformes, consultez Accessibilité.

Cette rubrique explique comment implémenter un fournisseur UI Automation côté serveur pour un contrôle personnalisé qui s'exécute dans une application WPF. WPF prend en charge l'UI Automation à travers une arborescence d'objets homologues Automation en parallèle à l'arborescence d'éléments de l'interface utilisateur. Le code de test et les applications qui fournissent des fonctionnalités d'accessibilité peuvent utiliser des objets homologues Automation directement (pour le code in-process) ou à travers l'interface généralisée fournie par l'UI Automation.

Cette rubrique comprend les sections suivantes.

  • Classes homologues Automation
  • Classes homologues Automation intégrées
  • Considérations sur la sécurité des homologues dérivés
  • Navigation homologue
  • Personnalisations dans un homologue dérivé
  • Rubriques connexes

Classes homologues Automation

Les contrôles WPF prennent en charge l'UI Automation à travers une arborescence de classes homologues qui dérivent de AutomationPeer. Par convention, les noms de classes homologues commencent par le nom de la classe de contrôle et finissent par "AutomationPeer". Par exemple, ButtonAutomationPeer est la classe homologue pour la classe de contrôle Button. Les classes homologues sont sensiblement équivalentes aux types de contrôle UI Automation, mais elles sont propres aux éléments WPF. Le code Automation qui accède aux applications WPF à travers l'interface UI Automation n'utilise pas directement les homologues Automation. En revanche, le code Automation qui se trouve dans le même espace de processus peut les utiliser directement.

Classes homologues Automation intégrées

Les éléments implémentent une classe homologue Automation s'ils acceptent l'activité d'interface de l'utilisateur ou s'ils contiennent les informations nécessaires aux utilisateurs d'applications de lecteur d'écran. Les éléments visuels WPF ne possèdent pas tous des homologues Automation. Des exemples de classes qui implémentent des homologues Automation sont Button, TextBox et Label. Des exemples de classes qui n'implémentent pas d'homologues Automation sont les classes qui dérivent de Decorator, telles que Border, et les classes basées sur Panel, telles que Grid et Canvas.

La classe Control de base ne possède pas de classe homologue correspondante. Si vous avez besoin de faire correspondre une classe homologue à un contrôle personnalisé qui dérive de Control, vous devez dériver la classe homologue personnalisée de FrameworkElementAutomationPeer.

Considérations sur la sécurité des homologues dérivés

Les homologues Automation doivent s'exécuter dans un environnement de confiance partielle. Le code de l'assembly UIAutomationClient n'étant pas configuré pour s'exécuter dans un environnement de confiance partielle, le code homologue Automation ne doit pas référencer cet assembly. À la place, utilisez les classes de l'assembly UIAutomationTypes. Par exemple, vous devez utiliser la classe AutomationElementIdentifiers de l'assembly UIAutomationTypes, qui correspond à la classe AutomationElement de l'assembly UIAutomationClient. Il est possible de référencer sans risque l'assembly UIAutomationTypes dans le code homologue Automation.

Après avoir localisé un homologue Automation, le code in-process peut naviguer dans l'arborescence homologue en appelant les méthodes GetChildren et GetParent de l'objet. Dans un contrôle, la navigation parmi les éléments WPF est prise en charge par l'implémentation de l'homologue de la méthode GetChildrenCore. Le système UI Automation appelle cette méthode pour générer une arborescence de sous-éléments contenus dans un contrôle ; par exemple, des éléments de liste dans une zone de liste. La méthode UIElementAutomationPeer.GetChildrenCore par défaut parcourt l'arborescence visuelle d'éléments pour générer l'arborescence d'homologues Automation. Les contrôles personnalisés substituent cette méthode pour exposer des éléments enfants aux clients Automation, en retournant les homologues Automation des éléments qui acheminent des informations ou autorisent l'intervention de l'utilisateur.

Personnalisations dans un homologue dérivé

Toutes les classes qui dérivent de UIElement et ContentElement contiennent la méthode virtuelle protégée OnCreateAutomationPeer. WPF appelle OnCreateAutomationPeer pour recevoir l'objet homologue Automation pour chaque contrôle. Le code Automation peut utiliser l'homologue pour obtenir des informations sur les caractéristiques et les fonctionnalités d'un contrôle et pour simuler une utilisation interactive. Un contrôle personnalisé qui prend en charge Automation doit substituer OnCreateAutomationPeer et retourner une instance d'une classe qui dérive de AutomationPeer. Par exemple, si un contrôle personnalisé est dérivé de la classe ButtonBase, alors l'objet retourné par OnCreateAutomationPeer doit dériver de ButtonBaseAutomationPeer.

Lorsque vous implémentez un contrôle personnalisé, vous devez substituer les méthodes "Core" de la classe homologue Automation de base qui décrivent le comportement unique et spécifique de votre contrôle personnalisé.

Substituer OnCreateAutomationPeer

Substituez la méthode OnCreateAutomationPeer pour votre contrôle personnalisé afin qu'il retourne votre objet fournisseur, qui doit dériver directement ou indirectement de AutomationPeer.

Substituer GetPattern

Les homologues Automation simplifient quelques aspects d'implémentation des fournisseurs UI Automation côté serveur, mais les homologues Automation de contrôle personnalisés doivent encore gérer des interfaces de modèle. Tout comme les fournisseurs non WPF, les homologues prennent en charge les modèles de contrôle en fournissant des implémentations d'interfaces dans l'espace de noms System.Windows.Automation.Provider, telles que IInvokeProvider. Les interfaces du modèle de contrôle peuvent être implémentées par l'homologue lui-même ou par un autre objet. L'implémentation de l'homologue de GetPattern retourne l'objet qui prend en charge le modèle spécifié. Le code UI Automation appelle la méthode GetPattern et spécifie une valeur de l'énumération PatternInterface. La substitution de GetPattern doit retourner l'objet qui implémente le modèle spécifié. Si votre contrôle ne possède pas une implémentation personnalisée d'un modèle, vous pouvez appeler l'implémentation du type de base de GetPattern pour récupérer son implémentation ou une référence null si le modèle n'est pas pris en charge pour ce type de contrôle. Par exemple, un contrôle NumericUpDown personnalisé peut avoir une valeur dans une plage de sorte que son homologue UI Automation implémente l'interface IRangeValueProvider. L'exemple suivant montre comment la méthode GetPattern de l'homologue est substituée pour répondre à une valeur PatternInterface.RangeValue.

        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
public override object GetPattern(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        return this;
    }
    return base.GetPattern(patternInterface);
}

Une méthode GetPattern peut également spécifier un sous-élément comme fournisseur de modèles. Le code suivant explique comment ItemsControl transfère la gestion du modèle du défilement à l'homologue de son contrôle ScrollViewer interne.

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

Pour spécifier un sous-élément en vue de la gestion de modèle, ce code obtient l'objet de sous-élément, crée un homologue en utilisant la méthode CreatePeerForElement, affecte la propriété EventsSource du nouvel homologue à l'homologue actuel et retourne le nouvel homologue. Affecter EventsSource à un sous-élément empêche le sous-élément d'apparaître dans l'arborescence homologue Automation et désigne tous les événements déclenchés par le sous-élément comme étant originaire du contrôle spécifié dans EventsSource. Le contrôle ScrollViewer n'apparaît pas dans l'arborescence Automation et les événements de défilement qu'il génère semblent provenir de l'objet ItemsControl.

Substituer les méthodes "Core"

Le code Automation obtient des informations sur votre contrôle en appelant des méthodes publiques de la classe homologue. Pour fournir des informations sur votre contrôle, substituez chaque méthode dont le nom termine par "Core" lorsque votre implémentation de contrôle diffère de celle fournie par la classe homologue Automation de base. Votre contrôle doit au minimum implémenter les méthodes GetClassNameCore et GetAutomationControlTypeCore, comme indiqué dans l'exemple suivant :

        Protected Overrides Function GetClassNameCore() As String
            Return "NumericUpDown"
        End Function

        Protected Overrides Function GetAutomationControlTypeCore() As AutomationControlType
            Return AutomationControlType.Spinner
        End Function
protected override string GetClassNameCore()
{
    return "NumericUpDown";
}

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

Votre implémentation de GetAutomationControlTypeCore décrit votre contrôle en retournant une valeur ControlType. Bien que vous puissiez retourner ControlType.Custom, vous devez retourner un des types de contrôles les plus spécifiques s'il décrit votre contrôle correctement. Une valeur de retour de ControlType.Custom exige du travail supplémentaire de la part du fournisseur pour implémenter l'UI Automation et les produits clients UI Automation ne peuvent pas anticiper la structure du contrôle, l'interaction du clavier et les modèles de contrôle éventuels.

Implémentez les méthodes IsContentElementCore et IsControlElementCore pour indiquer si votre contrôle contient des données ou s'il accomplit un rôle interactif dans l'interface utilisateur (ou les deux). Les deux méthodes retournent true par défaut. Ces paramètres facilitent l'utilisation des outils Automation, tels que les lecteurs d'écran qui peuvent utiliser ces méthodes pour filtrer l'arborescence Automation. Si votre méthode GetPattern transfère la gestion de modèle à un homologue de sous-élément, la méthode IsControlElementCore de l'homologue de sous-élément peut retourner la valeur false pour masquer l'homologue de sous-élément dans l'arborescence Automation. Par exemple, le défilement dans une ListBox est contrôlé par un ScrollViewer et l'homologue Automation pour PatternInterface.Scroll est retourné par la méthode GetPattern du ScrollViewerAutomationPeer associé au ListBoxAutomationPeer. Par conséquent, la méthode IsControlElementCore du ScrollViewerAutomationPeer retourne la valeur false afin que le ScrollViewerAutomationPeer n'apparaisse pas dans l'arborescence Automation.

Votre homologue Automation doit fournir des valeurs par défaut appropriées pour votre contrôle. Notez que le code XAML qui référence votre contrôle peut substituer vos implémentations homologues de méthodes "Core" en incluant des attributs AutomationProperties. Par exemple, le code XAML suivant crée un bouton possédant deux propriétés UI Automation personnalisées.

<Button AutomationProperties.Name="Special" 
    AutomationProperties.HelpText="This is a special button."/>

Implémenter les fournisseurs de modèles

Les interfaces implémentées par un fournisseur personnalisé sont déclarées explicitement si l'élément propriétaire dérive directement de Control. Par exemple, le code suivant déclare un homologue pour un Control qui implémente une valeur de plage.

public class RangePeer1 : FrameworkElementAutomationPeer, IRangeValueProvider { }
Public Class RangePeer1
    Inherits FrameworkElementAutomationPeer
    Implements IRangeValueProvider
End Class

Si le contrôle propriétaire dérive d'un type spécifique de contrôle tel que RangeBase, l'homologue peut être dérivé d'une classe homologue dérivée équivalente. Dans ce cas, l'homologue dériverait de RangeBaseAutomationPeer, qui fournit une implémentation de base de IRangeValueProvider. Le code suivant montre la déclaration d'un tel homologue.

public class RangePeer2 : RangeBaseAutomationPeer { }
Public Class RangePeer2
    Inherits RangeBaseAutomationPeer
End Class

Pour un exemple d'implémentation, consultez Contrôle personnalisé NumericUpDown avec thème et prise en charge d'UI Automation, exemple.

Déclencher des événements

Les clients Automation peuvent s'abonner à des événements Automation. Les contrôles personnalisés doivent signaler les changements apportés à l'état du contrôle en appelant la méthode RaiseAutomationEvent. De même, lorsqu'une valeur de propriété change, appelez la méthode RaisePropertyChangedEvent. Le code suivant indique comment obtenir l'objet homologue à partir du code de contrôle et appeler une méthode pour déclencher un événement. Par souci d'optimisation, le code détermine s'il existe des écouteurs pour ce type d'événement. Le fait de déclencher l'événement uniquement lorsqu'il existe des écouteurs évite les charges mémoire inutiles et permet au contrôle de rester sensible.

            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
if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
    NumericUpDownAutomationPeer peer = 
        UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;

    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}

Voir aussi

Concepts

Vue d'ensemble d'UI Automation

Implémentation de fournisseur UI Automation côté serveur

Autres ressources

Contrôle personnalisé NumericUpDown avec thème et prise en charge d'UI Automation, exemple (page éventuellement en anglais)

Historique des modifications

Date

Historique

Motif

Décembre 2010

Ajout d' exemples manquants de Visual Basic.

Résolution des bogues de contenu.

Juillet 2008

Ajout d'une rubrique.

Améliorations apportées aux informations.