Compartilhar via


Usar a Propriedade AutomationID

Observação

Esta documentação destina-se aos desenvolvedores do .NET Framework que desejam usar as classes de Automação de Interface do Usuário gerenciadas definidas no System.Windows.Automation namespace. Para obter as informações mais recentes sobre a Automação da Interface do Usuário, consulte a API de Automação do Windows: Automação da Interface do Usuário.

Este tópico contém cenários e código de exemplo que mostram como e quando o AutomationIdProperty pode ser usado para localizar um elemento na árvore de Automação da IU.

AutomationIdProperty identifica exclusivamente um elemento de Automação de Interface do Usuário entre seus elementos irmãos. Para obter mais informações sobre identificadores de propriedade relacionados à identificação de controle, consulte a visão geral das propriedades de automação da interface do usuário.

Observação

AutomationIdProperty não garante uma identidade única em toda a árvore; geralmente precisa de informações de contêiner e escopo para ser útil. Por exemplo, um aplicativo pode conter um controlador de menu, com vários itens de menu de nível superior, que, por sua vez, possuem vários itens de menu filho. Esses itens de menu secundários podem ser identificados por um esquema genérico como "Item1", "Item 2" e assim por diante, permitindo identificadores duplicados para crianças em itens de menu de nível superior.

Cenários

Foram identificados três cenários principais de aplicativos clientes de Automação de Interface do Usuário que exigem o uso de AutomationIdProperty para obter resultados precisos e consistentes ao pesquisar elementos.

Observação

AutomationIdProperty é compatível com todos os elementos de Automação da Interface do Usuário na exibição de controle, exceto janelas de aplicativo de nível superior, elementos de Automação de Interface do Usuário derivados de controles do Windows Presentation Foundation (WPF) que não têm uma ID ou x:Uid e elementos de Automação de Interface do Usuário derivados de controles Win32 que não têm uma ID de controle.

Usar uma AutomationID exclusiva e detectável para localizar um elemento específico na árvore de automação da interface do usuário.

  • Use uma ferramenta como o UI Spy para relatar o AutomationIdProperty elemento de interesse de uma interface do usuário. Esse valor pode então ser copiado e colado em um aplicativo cliente, como um script de teste para testes automatizados subsequentes. Essa abordagem reduz e simplifica o código necessário para identificar e localizar um elemento em runtime.

Cuidado

Em geral, você deve tentar obter apenas filhos diretos do RootElement. Uma busca por descendentes pode percorrer centenas ou até milhares de elementos, possivelmente resultando em um erro de estouro de pilha. Se você estiver tentando obter um elemento específico em um nível inferior, inicie sua pesquisa na janela do aplicativo ou em um contêiner em um nível inferior.

///--------------------------------------------------------------------
/// <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

Usar um caminho persistente para retornar a um AutomationElement identificado anteriormente

  • Os aplicativos cliente, desde scripts de teste simples até utilitários de registro e reprodução robustos, podem exigir acesso a elementos que atualmente não são instanciados, como uma caixa de diálogo aberta de arquivo ou um item de menu e, portanto, não existem na árvore de Automação da Interface do Usuário. Esses elementos só podem ser instanciados reproduzindo ou "reproduzindo", uma sequência específica de ações de interface do usuário por meio do uso de propriedades de Automação da Interface do Usuário, como AutomationID, padrões de controle e ouvintes de eventos.
///--------------------------------------------------------------------
/// <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

Usar um caminho relativo para retornar a um AutomationElement identificado anteriormente

  • Em determinadas circunstâncias, como a AutomationID só tem a garantia de ser exclusiva entre irmãos, vários elementos na árvore de Automação da Interface do Usuário podem ter valores de propriedade AutomationID idênticos. Nessas situações, os elementos podem ser identificados exclusivamente com base em um pai e, se necessário, em um avô. Por exemplo, um desenvolvedor pode fornecer uma barra de menus com vários itens, cada um contendo múltiplos itens de menu filho, onde os filhos são identificados com AutomationIDs sequenciais, como "Item1", "Item2" e assim por diante. Cada item de menu poderia ser identificado exclusivamente por sua AutomationID juntamente com a AutomationID de seu pai e, se necessário, seu avô.

Consulte também