Condividi tramite


Automazione UI per un controllo personalizzato WPF

Automazione dell'interfaccia utente offre un'interfaccia unica e generalizzata che i client di automazione possono utilizzare per esaminare o gestire le interfacce utente di un'ampia gamma di piattaforme e framework. Automazione dell'interfaccia utente consente sia al codice di assicurazione qualità (test) che alle applicazioni di accessibilità, come i lettori di schermo, di esaminare gli elementi dell'interfaccia utente e simulare l'interazione dell'utente con essi da altri codici. Per informazioni sull'automazione interfaccia utente in tutte le piattaforme, vedere Accessibilità.

Questo argomento descrive come implementare un provider di automazione interfaccia utente lato server per un controllo personalizzato eseguito in un'applicazione WPF. WPF supporta l'Automazione dell'Interfaccia Utente tramite un albero di oggetti di automazione coesistenti, parallela all'albero degli elementi dell'interfaccia utente. Il codice di test e le applicazioni che forniscono funzionalità di accessibilità possono utilizzare direttamente gli oggetti peer di automazione (per il codice in-process) o tramite l'interfaccia di automazione dell'utente generalizzata fornita.

Classi peer di automazione

I controlli WPF supportano l'automazione interfaccia utente tramite un albero di classi peer che derivano da AutomationPeer. Per convenzione, i nomi delle classi peer iniziano con il nome della classe di controllo e terminano con "AutomationPeer". Ad esempio, ButtonAutomationPeer è la classe pari per la classe di controllo Button. Le classi peer sono approssimativamente equivalenti ai tipi di controllo di automazione interfaccia utente, ma sono specifiche degli elementi WPF. Il codice di automazione che accede alle applicazioni WPF tramite l'interfaccia di automazione utente non usa direttamente i peer di automazione, ma il codice di automazione nello stesso spazio di processo può usare direttamente i peer di automazione.

Classi peer di automazione predefinite

Gli elementi implementano una classe peer di automazione se accettano interazioni tramite l'interfaccia dall'utente o se contengono informazioni necessarie per gli utenti di applicazioni per lettori di schermo. Non tutti gli elementi visivi WPF hanno corrispondenti di automazione. Esempi di classi che implementano peer di automazione sono Button, TextBoxe Label. Esempi di classi che non implementano peer di automazione sono classi che derivano da Decorator, ad esempio Border, e classi che sono basate su Panel, ad esempio Grid e Canvas.

La classe base Control non dispone di una classe peer corrispondente. Se è necessaria una classe peer per corrispondere a un controllo personalizzato che deriva da Control, è necessario derivare la classe peer personalizzata da FrameworkElementAutomationPeer.

Considerazioni sulla sicurezza per i pari derivati

I peer di automazione devono essere eseguiti in un ambiente di fiducia parziale. Il codice nell'assembly UIAutomationClient non è configurato per l'esecuzione in un ambiente parzialmente attendibile e il codice peer di automazione non deve fare riferimento a tale assembly. È invece consigliabile usare le classi nell'assembly UIAutomationTypes. Ad esempio, è consigliabile usare la classe AutomationElementIdentifiers dall'assembly UIAutomationTypes, che corrisponde alla classe AutomationElement nell'assembly UIAutomationClient. È sicuro fare riferimento all'assembly UIAutomationTypes nel codice peer di automazione.

Navigazione tra pari

Dopo aver individuato un peer di automazione, il codice in-process può spostarsi nell'albero peer chiamando i metodi GetChildren e GetParent dell'oggetto. Lo spostamento tra gli elementi WPF all'interno di un controllo è supportato dall'implementazione del peer del metodo GetChildrenCore. Il sistema di Automazione interfaccia utente chiama questo metodo per creare un albero di sottoelementi contenuti all'interno di un controllo; ad esempio elementi di elenco in una casella di riepilogo. Il metodo UIElementAutomationPeer.GetChildrenCore predefinito attraversa l'albero visivo degli elementi per compilare l'albero dei peer di automazione. I controlli personalizzati sovrascrivono questo metodo per esporre gli elementi figlio ai client di automazione, restituendo i peer di automazione degli elementi che forniscono informazioni o permettono l'interazione con l'utente.

Personalizzazioni in un peer derivato

Tutte le classi che derivano da UIElement e ContentElement contengono il metodo virtuale protetto OnCreateAutomationPeer. WPF chiama OnCreateAutomationPeer per ottenere l'oggetto peer di automazione per ogni controllo. Il codice di automazione può usare il peer per ottenere informazioni sulle caratteristiche e sulle funzionalità di un controllo e per simulare l'uso interattivo. Un controllo personalizzato che supporta l'automazione deve eseguire l'override di OnCreateAutomationPeer e restituire un'istanza di una classe che deriva da AutomationPeer. Ad esempio, se un controllo personalizzato deriva dalla classe ButtonBase, l'oggetto restituito da OnCreateAutomationPeer deve derivare da ButtonBaseAutomationPeer.

Quando si implementa un controllo personalizzato, è necessario eseguire l'override dei metodi "Core" dalla classe peer di automazione di base che descrivono il comportamento univoco e specifico del controllo personalizzato.

Eseguire l'override di OnCreateAutomationPeer

Eseguire l'override del metodo OnCreateAutomationPeer per il controllo personalizzato in modo che restituisca l'oggetto provider, che deve derivare direttamente o indirettamente da AutomationPeer.

Sovrascrivere GetPattern

I peer di automazione semplificano alcuni aspetti di implementazione dei provider di automazione interfaccia utente lato server, ma i peer di automazione dei controlli personalizzati devono comunque gestire le interfacce dei modelli. Analogamente ai provider non WPF, i peer supportano i modelli di controllo fornendo implementazioni di interfacce nello spazio dei nomi System.Windows.Automation.Provider, ad esempio IInvokeProvider. Le interfacce del pattern di controllo possono essere implementate dallo stesso peer o da un altro oggetto. L'implementazione del peer di GetPattern restituisce l'oggetto che supporta il modello specificato. Il codice di automazione interfaccia utente chiama il metodo GetPattern e specifica un valore di enumerazione PatternInterface. L'override di GetPattern deve restituire l'oggetto che implementa il modello specificato. Se il controllo non dispone di un'implementazione personalizzata di un pattern, è possibile chiamare l'implementazione del tipo di base di GetPattern per recuperare l'implementazione o null se il pattern non è supportato per questo tipo di controllo. Ad esempio, un controllo NumericUpDown personalizzato può essere impostato su un valore all'interno di un intervallo, affinché il peer di automazione dell'interfaccia utente implementi l'interfaccia IRangeValueProvider. Nell'esempio seguente viene illustrato come viene eseguito l'override del metodo GetPattern del peer per rispondere a un valore PatternInterface.RangeValue.

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

Un metodo GetPattern può anche specificare un sottoelemento come fornitore di modelli. Il codice seguente illustra come ItemsControl trasferisce la gestione del pattern di scorrimento al peer del proprio controllo ScrollViewer interno.

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  

Per specificare un sottoelemento per la gestione dei criteri, questo codice ottiene l'oggetto sottoelemento, crea un peer utilizzando il metodo CreatePeerForElement, imposta la proprietà EventsSource del nuovo peer sul peer corrente e restituisce il nuovo peer. L'impostazione di EventsSource su un sottoelemento impedisce la visualizzazione del sottoelemento nell'albero peer di automazione e definisce tutti gli eventi generati dal sottoelemento come originato dal controllo specificato in EventsSource. Il controllo ScrollViewer non viene visualizzato nell'albero di automazione e gli eventi di scorrimento generati sembrano provenire dall'oggetto ItemsControl.

Sovrascrivere i metodi "Core"

Il codice di automazione ottiene informazioni sul controllo chiamando i metodi pubblici della classe associata. Per fornire informazioni sul controllo, eseguire l'override di ogni metodo il cui nome termina con "Core" quando l'implementazione del controllo è diversa da quella fornita dalla classe peer di automazione di base. Come minimo, il controllo deve implementare i metodi GetClassNameCore e GetAutomationControlTypeCore, come illustrato nell'esempio seguente.

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

La tua implementazione di GetAutomationControlTypeCore descrive il tuo controllo restituendo un valore ControlType. Sebbene sia possibile restituire ControlType.Custom, è consigliabile restituire uno dei tipi di controllo più specifici se descrive in modo accurato il controllo. Un valore restituito di ControlType.Custom richiede un lavoro aggiuntivo per il provider per implementare l'automazione interfaccia utente e i prodotti client di automazione interfaccia utente non sono in grado di prevedere la struttura di controllo, l'interazione tramite tastiera e i possibili modelli di controllo.

Implementare i metodi IsContentElementCore e IsControlElementCore per indicare se il controllo contiene contenuto di dati o svolge un ruolo interattivo nell'interfaccia utente (o entrambi). Per impostazione predefinita, entrambi i metodi restituiscono true. Queste impostazioni migliorano l'usabilità degli strumenti di automazione, ad esempio le utilità per la lettura dello schermo, che possono usare questi metodi per filtrare l'albero di automazione. Se il metodo GetPattern trasferisce la gestione dei modelli a un peer del sottoelemento, il metodo IsControlElementCore del peer del sottoelemento può restituire false per nascondere il peer del sottoelemento dall'albero di automazione. Ad esempio, lo scorrimento in un ListBox viene gestito da un ScrollViewere il peer di automazione per PatternInterface.Scroll viene restituito dal metodo GetPattern del ScrollViewerAutomationPeer associato al ListBoxAutomationPeer. Pertanto, il metodo IsControlElementCore del ScrollViewerAutomationPeer restituisce false, in modo che l'ScrollViewerAutomationPeer non venga visualizzato nell'albero di automazione.

Il peer di automazione deve fornire valori predefiniti appropriati per il controllo. Tieni presente che XAML con riferimento al tuo controllo può eseguire l'override delle implementazioni peer dei metodi principali includendo attributi AutomationProperties. Ad esempio, il codice XAML seguente crea un pulsante con due proprietà personalizzate di automazione interfaccia utente.

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

Implementare provider di modelli

Le interfacce implementate da un provider personalizzato vengono dichiarate in modo esplicito se l'elemento proprietario deriva direttamente da Control. Ad esempio, il codice seguente dichiara un peer per un Control che implementa un valore di intervallo.

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

Se il controllo proprietario deriva da un tipo specifico di controllo, ad esempio RangeBase, il peer può essere derivato da una classe peer derivata equivalente. In questo caso, il peer deriva da RangeBaseAutomationPeer, che fornisce un'implementazione di base di IRangeValueProvider. Il codice seguente illustra la dichiarazione di tale peer.

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

Per un'implementazione di esempio, vedere C# o codice sorgente di Visual Basic che implementa e utilizza un controllo personalizzato NumericUpDown.

Generare eventi

I client di automazione possono iscriversi a eventi di automazione. I controlli personalizzati devono segnalare modifiche allo stato di controllo chiamando il metodo RaiseAutomationEvent. Analogamente, quando un valore della proprietà cambia, chiamare il metodo RaisePropertyChangedEvent. Il codice seguente illustra come ottenere l'oggetto peer dal codice di controllo e chiamare un metodo per generare un evento. Come ottimizzazione, il codice determina se sono presenti listener per questo tipo di evento. Lanciare l'evento solo quando ci sono ascoltatori evita un sovraccarico non necessario e aiuta il controllo a rimanere reattivo.

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

Vedere anche