Freigeben über


Benutzeroberflächenautomatisierung eines benutzerdefinierten WPF-Steuerelements

Die Benutzeroberflächenautomatisierung bietet eine einzelne, generalisierte Schnittstelle, die Automatisierungsclients verwenden können, um die Benutzeroberflächen einer Vielzahl von Plattformen und Frameworks zu untersuchen oder zu bedienen. Die Benutzeroberflächenautomatisierung ermöglicht sowohl Qualitätssicherungs- (Test-) Code als auch Barrierefreiheitsanwendungen wie Bildschirmlesegeräte, Benutzeroberflächenelemente zu untersuchen und die Benutzerinteraktion mit ihnen aus anderem Code zu simulieren. Informationen zur Benutzeroberflächenautomatisierung auf allen Plattformen finden Sie unter Barrierefreiheit.

In diesem Thema wird beschrieben, wie Sie einen serverseitigen Benutzeroberflächenautomatisierungs-Anbieter für ein benutzerdefiniertes Steuerelement implementieren, das in einer WPF-Anwendung ausgeführt wird. WPF unterstützt die Benutzeroberflächenautomatisierung über eine Struktur von Peerautomatisierungsobjekten, die die Struktur der Benutzeroberflächenelemente paralleliert. Testcode und Anwendungen, die Barrierefreiheitsfeatures bereitstellen, können Automatisierungspeerobjekte direkt (für Prozesscode) oder über die von der Benutzeroberflächenautomatisierung bereitgestellte generalisierte Schnittstelle verwenden.

Automatisierungspeerklassen

WPF-Steuerelemente unterstützen die UI-Automatisierung durch eine Baumstruktur von Peerklassen, die von AutomationPeer abgeleitet sind. Üblicherweise beginnen die Namen von Peer-Klassen mit dem Namen der Steuerelementklasse und enden mit "AutomationPeer". Beispielsweise ist ButtonAutomationPeer die Peerklasse der Button-Steuerungsklasse. Die Peerklassen entsprechen ungefähr den Steuerelementtypen der Benutzeroberflächenautomatisierung, sind jedoch spezifisch für WPF-Elemente. Automatisierungscode, der über die Benutzeroberflächenautomatisierungs-Schnittstelle auf WPF-Anwendungen zugreift, verwendet keine Automatisierungspeers direkt, aber Automatisierungscode im selben Prozessbereich kann Automatisierungspeers direkt verwenden.

Integrierte Automatisierungspeerklassen

Elemente implementieren eine Automatisierungspeerklasse, wenn sie Benutzeroberflächenaktivitäten akzeptieren oder Informationen enthalten, die von Benutzern von Sprachausgabeanwendungen benötigt werden. Nicht alle visuellen WPF-Elemente verfügen über Automatisierungspeers. Beispiele für Klassen, die Automatisierungspeers implementieren, sind Button, TextBoxund Label. Beispiele für Klassen, die keine Automatisierungspeers implementieren, sind Klassen, die von Decorator abgeleitet sind, wie z. B. Border, und Klassen, die auf Panel basieren, wie z. B. Grid und Canvas.

Die Basisklasse Control verfügt nicht über eine entsprechende Peerklasse. Wenn Sie eine Peerklasse benötigen, die einem benutzerdefinierten Steuerelement entspricht, das von Control abgeleitet ist, sollten Sie die benutzerdefinierte Peerklasse von FrameworkElementAutomationPeer ableiten.

Sicherheitsüberlegungen für abgeleitete Peers

Automatisierungspeers müssen in einer teilweise vertrauenswürdigen Umgebung ausgeführt werden. Code in der UIAutomationClient-Assembly ist nicht für die Ausführung in einer teilweise vertrauenswürdigen Umgebung konfiguriert, und Automatisierungspeercode sollte nicht auf diese Assembly verweisen. Stattdessen sollten Sie die Klassen in der UIAutomationTypes-Assembly verwenden. Sie sollten beispielsweise die AutomationElementIdentifiers Klasse aus der UIAutomationTypes-Assembly verwenden, die der AutomationElement Klasse in der UIAutomationClient-Assembly entspricht. Es ist sicher, auf die UIAutomationTypes-Assembly im Automatisierungspeercode zu verweisen.

Peernavigation

Nach dem Auffinden eines Automatisierungspeers kann prozessinterner Code in der Peerstruktur navigieren, indem die Methoden GetChildren und GetParent des Objekts aufgerufen werden. Die Navigation zwischen WPF-Elementen innerhalb eines WPF-Steuerelements wird durch die Implementierung der Methode GetChildrenCore des Peers unterstützt. Das Benutzeroberflächenautomatisierungssystem ruft diese Methode auf, um eine Struktur von Unterelementen zu erstellen, die in einem Steuerelement enthalten sind; Beispielsweise Listenelemente in einem Listenfeld. Die Standardmethode UIElementAutomationPeer.GetChildrenCore durchläuft den visuellen Baum von Elementen, um den Baum der Automatisierungsobjekte zu erstellen. Benutzerdefinierte Steuerelemente überschreiben diese Methode, um untergeordnete Elemente für Automatisierungsclients verfügbar zu machen und die Automatisierungspeers von Elementen zurückzugeben, die Informationen vermitteln oder Benutzerinteraktionen zulassen.

Anpassungen in einem abgeleiteten Peer

Alle Klassen, die von UIElement und ContentElement abgeleitet sind, enthalten die geschützte virtuelle Methode OnCreateAutomationPeer. WPF ruft OnCreateAutomationPeer auf, um das Automatisierungspeerobjekt für jedes Steuerelement abzurufen. Automatisierungscode kann den Peer verwenden, um Informationen zu den Merkmalen und Features eines Steuerelements abzurufen und die interaktive Verwendung zu simulieren. Ein benutzerdefiniertes Steuerelement, das die Automatisierung unterstützt, muss OnCreateAutomationPeer übersteuern und eine Instanz einer Klasse zurückgeben, die von AutomationPeer abgeleitet ist. Wenn zum Beispiel ein benutzerdefiniertes Steuerelement von der Klasse ButtonBase abgeleitet ist, sollte das zurückgegebene Objekt von OnCreateAutomationPeer abgeleitet werden.

Bei der Implementierung eines benutzerdefinierten Steuerelements müssen Sie die "Core"-Methoden aus der Basisautomatisierungspeerklasse außer Kraft setzen, die das für Ihr benutzerdefiniertes Steuerelement eindeutige und spezifische Verhalten beschreiben.

Außerkraftsetzen von OnCreateAutomationPeer

Überschreiben Sie die OnCreateAutomationPeer-Methode für Ihr benutzerdefiniertes Steuerelement, sodass sie Ihr Anbieterobjekt zurückgibt, das direkt oder indirekt von AutomationPeer abgeleitet ist.

AußerKraftsetzen von GetPattern

Automatisierungspeers vereinfachen einige Implementierungsaspekte von serverseitigen Benutzeroberflächenautomatisierungs-Anbietern, aber benutzerdefinierte Steuerelementautomatisierungspeers müssen weiterhin Musterschnittstellen verarbeiten. Wie bei Nicht-WPF-Anbietern unterstützen Peers Steuerungsmuster, indem sie Implementierungen von Schnittstellen im System.Windows.Automation.Provider-Namespace bereitstellen, wie IInvokeProvider. Die Schnittstellen von Steuerelementmustern können vom Peer selbst oder von einem anderen Objekt implementiert werden. Die Implementierung des GetPattern Peers gibt das Objekt zurück, das das angegebene Muster unterstützt. Der Benutzeroberflächenautomatisierungs-Code ruft die GetPattern Methode auf und gibt einen PatternInterface Enumerationswert an. Ihre Überschreibung von GetPattern sollte das Objekt zurückgeben, das das spezifizierte Muster implementiert. Wenn Ihr Steuerelement nicht über eine benutzerdefinierte Implementierung eines Musters verfügt, können Sie die Implementierung GetPattern des Basistyps aufrufen, um entweder die Implementierung oder NULL abzurufen, wenn das Muster für diesen Steuerelementtyp nicht unterstützt wird. Beispielsweise kann ein benutzerdefiniertes NumericUpDown-Steuerelement auf einen Wert innerhalb eines Bereichs festgelegt werden, sodass sein Benutzeroberflächenautomatisierungspeer die IRangeValueProvider Schnittstelle implementieren würde. Das folgende Beispiel zeigt, wie die GetPattern-Methode des Peers überschrieben wird, sodass sie auf einen PatternInterface.RangeValue-Wert reagiert.

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

Eine GetPattern Methode kann auch ein Unterelement als Musteranbieter angeben. Der folgende Code zeigt, wie ItemsControl die Handhabung der Bildlaufmuster an den Peer des internen ScrollViewer Steuerelements überträgt.

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

Um ein Unterelement für die Musterbehandlung anzugeben, ruft dieser Code das Unterelementobjekt ab, erstellt einen Peer mithilfe der CreatePeerForElement Methode, legt die EventsSource Eigenschaft des neuen Peers auf den aktuellen Peer fest und gibt den neuen Peer zurück. Das Festlegen von EventsSource bei einem Unterelement verhindert, dass das Unterelement in der Automatisierungs-Peer-Struktur angezeigt wird, und weist alle vom Unterelement ausgelösten Ereignisse dem Steuerelement zu, das in EventsSource angegeben ist. Das ScrollViewer Steuerelement wird nicht in der Automatisierungsstruktur angezeigt, und Bildlaufereignisse, die es generiert, scheinen dem ItemsControl Objekt zu entstammen.

Außerkraftsetzen von "Core"-Methoden

Automatisierungscode ruft Informationen zu Ihrem Steuerelement ab, indem öffentliche Methoden der Peerklasse aufgerufen werden. Um Informationen zu Ihrem Steuerelement bereitzustellen, überschreiben Sie jede Methode, deren Name mit "Core" endet, wenn sich die Steuerelementimplementierung von der von der Basisautomatisierungspeerklasse bereitgestellten unterscheidet. Ihr Steuerelement muss mindestens die Methoden GetClassNameCore und GetAutomationControlTypeCore implementieren, wie im folgenden Beispiel gezeigt.

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

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

Protected Overrides Function GetAutomationControlTypeCore() As AutomationControlType
    Return AutomationControlType.Spinner
End Function

Ihre Implementierung von GetAutomationControlTypeCore beschreibt Ihr Steuerelement, indem sie einen ControlType-Wert zurückgibt. Obwohl Sie ControlType.Custom zurückgeben können, sollten Sie einen der spezifischeren Steuerelementtypen zurückgeben, wenn er Ihr Steuerelement genau beschreibt. Ein Rückgabewert ControlType.Custom erfordert zusätzliche Arbeit für den Anbieter zum Implementieren der Benutzeroberflächenautomatisierung, und Benutzeroberflächenautomatisierungs-Clientprodukte können die Steuerelementstruktur, Tastaturinteraktion und mögliche Steuerelementmuster nicht antizipieren.

Implementieren Sie die IsContentElementCore Methoden und IsControlElementCore Methoden, um anzugeben, ob Ihr Steuerelement Dateninhalte enthält oder eine interaktive Rolle auf der Benutzeroberfläche (oder beides) erfüllt. Standardmäßig geben beide Methoden true zurück. Diese Einstellungen erhöhen die Bedienbarkeit von Automatisierungswerkzeugen wie Screenreadern, die diese Methoden möglicherweise nutzen, um die Automatisierungsstruktur zu filtern. Wenn die GetPattern-Methode die Musterbehandlung an einen Unterelement-Peer überträgt, kann die Methode des Unterelement-Peers IsControlElementCore false zurückgeben, um den Unterelement-Peer aus der Automatisierungsstruktur auszublenden. Beispielsweise wird das Scrollen in einem ListBox von einem ScrollViewer gehandhabt, und der Automatisierungspeer für PatternInterface.Scroll wird durch die GetPattern-Methode des mit ScrollViewerAutomationPeer assoziierten ListBoxAutomationPeer zurückgegeben. Daher gibt die IsControlElementCore-Methode von ScrollViewerAutomationPeerfalse zurück, sodass ScrollViewerAutomationPeer nicht in der Automatisierungsstruktur erscheint.

Ihr Automatisierungspeer sollte geeignete Standardwerte für Ihr Steuerelement bereitstellen. Beachten Sie, dass XAML, das auf Ihr Steuerelement verweist, Ihre Peerimplementierungen von Kernmethoden überschreiben kann, indem Attribute wie AutomationProperties eingeschlossen werden. Mit dem folgenden XAML-Code wird beispielsweise eine Schaltfläche mit zwei angepassten Benutzeroberflächenautomatisierungseigenschaften erstellt.

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

Musteranbieter implementieren

Die von einem benutzerdefinierten Anbieter implementierten Schnittstellen werden explizit deklariert, wenn das besitzende Element direkt von Control ableitet. Beispielsweise deklariert der folgende Code einen Peer für einen Control, der einen Bereichswert implementiert.

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

Wenn das besitzende Steuerelement von einem bestimmten Typ wie RangeBase abgeleitet ist, kann der Peer von einer äquivalenten abgeleiteten Peerklasse stammen. In diesem Fall würde der Peer von RangeBaseAutomationPeer abgeleitet werden, das eine Basisimplementierung von IRangeValueProvider bereitstellt. Der folgende Code zeigt die Deklaration eines solchen Peers.

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

Eine Beispielimplementierung finden Sie im C# - oder Visual Basic-Quellcode , der ein benutzerdefiniertes NumericUpDown-Steuerelement implementiert und verwendet.

Auslösen von Ereignissen

Automatisierungsclients können sich für Automatisierungsereignisse registrieren. Benutzerdefinierte Steuerelemente müssen Änderungen am Steuerelementstatus melden, indem die RaiseAutomationEvent Methode aufgerufen wird. Wenn sich ein Eigenschaftswert ändert, rufen Sie die RaisePropertyChangedEvent Methode auf. Der folgende Code zeigt, wie das Peerobjekt aus dem Steuerelementcode abgerufen und eine Methode zum Auslösen eines Ereignisses aufgerufen wird. Als Optimierung bestimmt der Code, ob Listener für diesen Ereignistyp vorhanden sind. Das Ereignis wird nur ausgelöst, wenn Listener vorhanden sind, wodurch unnötiger Aufwand vermieden und das Steuerelement reaktionsfähig bleibt.

if (AutomationPeer.ListenerExists(AutomationEvents.PropertyChanged))
{
    NumericUpDownAutomationPeer peer =
        UIElementAutomationPeer.FromElement(nudCtrl) as NumericUpDownAutomationPeer;

    if (peer != null)
    {
        peer.RaisePropertyChangedEvent(
            RangeValuePatternIdentifiers.ValueProperty,
            (double)oldValue,
            (double)newValue);
    }
}
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

Siehe auch