Utilizzare la proprietà AutomationID

Nota

Questa documentazione è destinata agli sviluppatori .NET Framework che desiderano utilizzare le classi di Automazione interfaccia utente gestite definite nello spazio dei nomi System.Windows.Automation. Per informazioni aggiornate su Automazione interfaccia utente, vedere API di automazione di Windows: Automazione interfaccia utente.

Questo argomento contiene scenari e codice di esempio che illustrano come e quando è possibile usare AutomationIdProperty per individuare un elemento all'interno dell'albero di Automazione interfaccia utente.

AutomationIdProperty identifica in modo univoco un elemento di automazione interfaccia utente dagli elementi di pari livello. Per altre informazioni sugli identificatori di proprietà correlati all'identificazione dei controlli, vedere UI Automation Properties Overview.

Nota

AutomationIdProperty non garantisce un'identità univoca in tutto l'albero. Per essere utile, richiede in genere informazioni sui contenitori e sull'ambito. Ad esempio, un'applicazione può contenere un controllo menu con più voci di menu di livello superiore che, a loro volta, contengono più voci di menu figlio. Queste voci di menu secondarie possono essere identificate da uno schema generico, ad esempio "Item1", "Item 2" e così via, consentendo la presenza di identificatori duplicati per gli elementi figlio nelle voci di menu di primo livello.

Scenari

Sono stati identificati tre scenari principali dell'applicazione client di automazione interfaccia utente, che richiedono l'uso di AutomationIdProperty per ottenere risultati accurati e coerenti quando si cercano elementi.

Nota

AutomationIdProperty è supportato da tutti gli elementi di Automazione interfaccia utente nella vista controllo, ad eccezione delle finestre dell'applicazione di primo livello, degli elementi di Automazione interfaccia utente derivati da controlli WPF (Windows Presentation Foundation) che non hanno un ID o x:Uid e degli elementi di Automazione interfaccia utente derivati da controlli Win32 che non hanno un ID di controllo.

Usare un oggetto AutomationID univoco e individuabile per trovare un elemento specifico nell'albero di automazione interfaccia utente

  • Usare uno strumento come UI Spy per segnalare AutomationIdProperty di un elemento di interesse dell'interfaccia utente. Questo valore può quindi essere copiato e incollato in un'applicazione client, ad esempio uno script di test, per il successivo test automatizzato. Questo approccio riduce e semplifica il codice necessario per identificare e individuare un elemento in fase di esecuzione.

Attenzione

In generale, è consigliabile cercare di ottenere solo elementi figlio diretti di RootElement. Una ricerca dei discendenti può scorrere centinaia o anche migliaia di elementi, con la possibilità che venga generato un overflow dello stack. Per ottenere un elemento specifico a un livello inferiore, è consigliabile avviare la ricerca dalla finestra dell'applicazione o da un contenitore a un livello inferiore.

///--------------------------------------------------------------------
/// <summary>
/// Finds all elements in the UI Automation tree that have a specified
/// AutomationID.
/// </summary>
/// <param name="targetApp">
/// The root element from which to start searching.
/// </param>
/// <param name="automationID">
/// The AutomationID value of interest.
/// </param>
/// <returns>
/// The collection of UI Automation elements that have the specified
/// AutomationID value.
/// </returns>
///--------------------------------------------------------------------
private AutomationElementCollection FindElementFromAutomationID(AutomationElement targetApp,
    string automationID)
{
    return targetApp.FindAll(
        TreeScope.Descendants,
        new PropertyCondition(AutomationElement.AutomationIdProperty, automationID));
}
'''--------------------------------------------------------------------
''' <summary>
''' Finds all elements in the UI Automation tree that have a specified
''' AutomationID.
''' </summary>
''' <param name="targetApp">
''' The root element from which to start searching.
''' </param>
''' <param name="automationID">
''' The AutomationID value of interest.
''' </param>
''' <returns>
''' The collection of automation elements that have the specified
''' AutomationID value.
''' </returns>
'''--------------------------------------------------------------------
Private Function FindElementFromAutomationID( _
ByVal targetApp As AutomationElement, _
ByVal automationID As String) As AutomationElementCollection
    Return targetApp.FindAll( _
    TreeScope.Descendants, _
    New PropertyCondition( _
    AutomationElement.AutomationIdProperty, automationID))
End Function 'FindElementFromAutomationID

Usare un percorso persistente per tornare a un oggetto AutomationElement identificato in precedenza

  • Le applicazioni client, dai semplici script di test alle solide utilità di registrazione e riproduzione, potrebbero richiedere l'accesso a elementi di cui non è ancora stata creata un'istanza, ad esempio una finestra di dialogo di apertura file o una voce di menu, e che quindi non esistono nell'albero di automazione interfaccia utente. È possibile creare un'istanza di questi elementi solo riproducendo una sequenza specifica di azioni dell'interfaccia utente attraverso l'uso di proprietà di Automazione interfaccia utente come AutomationID, criteri di controllo e listener di eventi.
///--------------------------------------------------------------------
/// <summary>
/// Creates a UI Automation thread.
/// </summary>
/// <param name="sender">Object that raised the event.</param>
/// <param name="e">Event arguments.</param>
/// <remarks>
/// UI Automation must be called on a separate thread if the client
/// application itself could become a target for event handling.
/// For example, focus tracking is a desktop event that could involve
/// the client application.
/// </remarks>
///--------------------------------------------------------------------
private void CreateUIAThread(object sender, EventArgs e)
{
    // Start another thread to do the UI Automation work.
    ThreadStart threadDelegate = new ThreadStart(CreateUIAWorker);
    Thread workerThread = new Thread(threadDelegate);
    workerThread.Start();
}

///--------------------------------------------------------------------
/// <summary>
/// Delegated method for ThreadStart. Creates a UI Automation worker
/// class that does all UI Automation related work.
/// </summary>
///--------------------------------------------------------------------
public void CreateUIAWorker()
{
   uiautoWorker = new FindByAutomationID(targetApp);
}
private FindByAutomationID uiautoWorker;

'''--------------------------------------------------------------------
''' <summary>
''' Creates a UI Automation thread.
''' </summary>
''' <param name="sender">Object that raised the event.</param>
''' <param name="e">Event arguments.</param>
''' <remarks>
''' UI Automation must be called on a separate thread if the client
''' application itself could become a target for event handling.
''' For example, focus tracking is a desktop event that could involve
''' the client application.
''' </remarks>
'''--------------------------------------------------------------------
Private Sub CreateUIAThread(ByVal sender As Object, ByVal e As EventArgs)

    ' Start another thread to do the UI Automation work.
    Dim threadDelegate As New ThreadStart(AddressOf CreateUIAWorker)
    Dim workerThread As New Thread(threadDelegate)
    workerThread.Start()

End Sub


'''--------------------------------------------------------------------
''' <summary>
''' Delegated method for ThreadStart. Creates a UI Automation worker
''' class that does all UI Automation related work.
''' </summary>
'''--------------------------------------------------------------------
Public Sub CreateUIAWorker()

    uiautoWorker = New UIAWorker(targetApp)

End Sub

Private uiautoWorker As UIAWorker

///--------------------------------------------------------------------
/// <summary>
/// Function to playback through a series of recorded events calling
/// a WriteToScript function for each event of interest.
/// </summary>
/// <remarks>
/// A major drawback to using AutomationID for recording user
/// interactions in a volatile UI is the probability of catastrophic
/// change in the UI. For example, the //Processes// dialog where items
/// in the listbox container can change with no input from the user.
/// This mandates that a record and playback application must be
/// reliant on the tester owning the UI being tested. In other words,
/// there has to be a contract between the provider and client that
/// excludes uncontrolled, external applications. The added benefit
/// is the guarantee that each control in the UI should have an
/// AutomationID assigned to it.
///
/// This function relies on a UI Automation worker class to create
/// the System.Collections.Generic.Queue object that stores the
/// information for the recorded user interactions. This
/// allows post-processing of the recorded items prior to actually
/// writing them to a script. If this is not necessary the interaction
/// could be written to the script immediately.
/// </remarks>
///--------------------------------------------------------------------
private void Playback(AutomationElement targetApp)
{
    AutomationElement element;
    foreach(ElementStore storedItem in uiautoWorker.elementQueue)
    {
        PropertyCondition propertyCondition =
            new PropertyCondition(
            AutomationElement.AutomationIdProperty, storedItem.AutomationID);
        // Confirm the existence of a control.
        // Depending on the controls and complexity of interaction
        // this step may not be necessary or may require additional
        // functionality. For example, to confirm the existence of a
        // child menu item that had been invoked the parent menu item
        // would have to be expanded.
        element = targetApp.FindFirst(TreeScope.Descendants, propertyCondition);
        if(element == null)
        {
            // Control not available, unable to continue.
            // TODO: Handle error condition.
            return;
        }
        WriteToScript(storedItem.AutomationID, storedItem.EventID);
    }
}

///--------------------------------------------------------------------
/// <summary>
/// Generates script code and outputs the code to a text control in
/// the client.
/// </summary>
/// <param name="automationID">
/// The AutomationID of the current control.
/// </param>
/// <param name="eventID">
/// The event recorded on that control.
/// </param>
///--------------------------------------------------------------------
private void WriteToScript(string automationID, string eventID)
{
    // Script code would be generated and written to an output file
    // as plain text at this point, but for the
    // purposes of this example we just write to the console.
    Console.WriteLine(automationID + " - " + eventID);
}
'''--------------------------------------------------------------------
''' <summary>
''' Function to playback through a series of recorded events calling
''' a WriteToScript function for each event of interest.
''' </summary>
''' <remarks>
''' A major drawback to using AutomationID for recording user
''' interactions in a volatile UI is the probability of catastrophic
''' change in the UI. For example, the 'Processes' dialog where items
''' in the listbox container can change with no input from the user.
''' This mandates that a record and playback application must be
''' reliant on the tester owning the UI being tested. In other words,
''' there has to be a contract between the provider and client that
''' excludes uncontrolled, external applications. The added benefit
''' is the guarantee that each control in the UI should have an
''' AutomationID assigned to it.
'''
''' This function relies on a UI Automation worker class to create
''' the System.Collections.Generic.Queue object that stores the
''' information for the recorded user interactions. This
''' allows post-processing of the recorded items prior to actually
''' writing them to a script. If this is not necessary the interaction
''' could be written to the script immediately.
''' </remarks>
'''--------------------------------------------------------------------
Private Sub Playback(ByVal targetApp As AutomationElement)

    Dim element As AutomationElement
    Dim storedItem As ElementStore
    For Each storedItem In uiautoWorker.elementQueue
        Dim propertyCondition As New PropertyCondition( _
        AutomationElement.AutomationIdProperty, storedItem.AutomationID)
        ' Confirm the existence of a control.
        ' Depending on the controls and complexity of interaction
        ' this step may not be necessary or may require additional
        ' functionality. For example, to confirm the existence of a
        ' child menu item that had been invoked the parent menu item
        ' would have to be expanded.
        element = targetApp.FindFirst( _
        TreeScope.Descendants, propertyCondition)
        If element Is Nothing Then
            ' Control not available, unable to continue.
            ' TODO: Handle error condition.
            Return
        End If
        WriteToScript(storedItem.AutomationID, storedItem.EventID)
    Next storedItem

End Sub


'''--------------------------------------------------------------------
''' <summary>
''' Generates script code and outputs the code to a text control in
''' the client.
''' </summary>
''' <param name="automationID">
''' The AutomationID of the current control.
''' </param>
''' <param name="eventID">
''' The event recorded on that control.
''' </param>
'''--------------------------------------------------------------------
Private Sub WriteToScript( _
ByVal automationID As String, ByVal eventID As String)

    ' Script code would be generated and written to an output file
    ' as plain text at this point, but for the
    ' purposes of this example we just write to the console.
    Console.WriteLine(automationID + " - " + eventID)

End Sub

Usare un percorso relativo per tornare a un oggetto AutomationElement identificato in precedenza

  • In alcune circostanze, poiché è garantito che AutomationID sia univoco solo tra gli elementi di pari livello, più elementi dell'albero di automazione interfaccia utente possono avere valori identici per la proprietà AutomationID. In questi casi, gli elementi possono essere identificati in modo univoco in base a un elemento padre e, se necessario, a un elemento padre del padre. Uno sviluppatore, ad esempio, potrebbe fornire una barra dei menu con più voci di menu, tutte con più voci di menu figlio, dove gli elementi figlio vengono identificati con AutomationID sequenziali, ad esempio "Item1", "Item2" e così via. Ogni voce di menu può quindi essere identificata in modo univoco dal rispettivo AutomationID insieme all'AutomationID del padre e, se necessario, del padre del padre.

Vedi anche