Поделиться через


Использование свойства AutomationID

ПримечаниеПримечание

Эта документация предназначена для разработчиков на платформе .NET Framework, которым требуется использовать управляемые классы UI Automation, определенные в пространстве имен System.Windows.Automation.Последние сведения о UI Automation см. на веб-странице Windows Automation API: UI Automation.

В этом разделе содержатся сценарии и примеры кода, которые показывают, как и когда можно использовать AutomationIdProperty для поиска элемента в дереве UI Automation.

AutomationIdProperty однозначно определяет Элемент модели автоматизации пользовательского интерфейса из его родственных элементов того же уровня. Дополнительные сведения об идентификаторах свойств, связанных с идентификацией элементов управления, см. в разделе Общие сведения о свойствах автоматизированного пользовательского интерфейса.

ПримечаниеПримечание

AutomationIdProperty не гарантирует уникальной идентификации во всем дереве; как правило, для полезной деятельности ему нужны сведения о контейнере и области видимости.Например, приложение может содержать элемент управления меню с несколькими элементами меню верхнего уровня, которые, в свою очередь, имеют несколько дочерних элементов меню.Эти элементы меню второго уровня могут идентифицироваться по универсальной схеме: "Item1", "Item2" и т. д., при этом разрешены повторяющиеся имена потомков элементов меню верхнего уровня.

Сценарии

Были определены три основных скрипта приложений клиентов автоматизации пользовательского интерфейса, требующие использования AutomationIdProperty для получения точных и согласованных результатов при поиске элементов.

ПримечаниеПримечание

AutomationIdProperty поддерживается всеми элементами автоматизации пользовательского интерфейса в представлении элемента управления, за исключением приложений верхнего уровня, элементов автоматизации пользовательского интерфейса, производных от элементов управления Windows Presentation Foundation (WPF), не имеющих идентификатора ID или x:UID, и элементов автоматизации пользовательского интерфейса, производных от элементов управления Win32, не имеющих идентификатора ID элементов управления.

Использование уникального и доступного для обнаружения AutomationID для поиска определенного элемента в дереве UI Automation

  • Следует воспользоваться таким средством, как UI Spy, чтобы сообщить AutomationIdProperty о необходимом элементе UI. Это значение затем можно скопировать и вставить в клиентское приложение, например, в тестовый скрипт для последующей автоматического тестирования. Такой подход сокращает и упрощает код, необходимый для идентификации и обнаружения элементов во время выполнения приложения.
Предупреждающее замечаниеВнимание

В общем случае необходимо попытаться получить только прямых потомков RootElement.В процессе поиска потомков выполняется итерация сотен или даже тысяч элементов, что может привести к переполнению стека.При попытке получить определенный элемент на более низком уровне, необходимо запустить поиск из окна приложения или из контейнера на более низком уровне.

'''--------------------------------------------------------------------
''' <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
///--------------------------------------------------------------------
/// <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));
}

Использование постоянного пути для возврата к ранее идентифицированному AutomationElement

  • Клиентским приложениям, от простых тестовых скриптов до надежных служебных программ записи и воспроизведения, может потребоваться доступ к элементам, не инициализированным в данный момент (например, к диалогу открытия файла или к элементу меню), и поэтому не существующих в дереве модели автоматизации пользовательского интерфейса. Экземпляры этих элементов могут быть созданы только воспроизведением, или "проигрыванием", определенной последовательности действий user interface (UI) с использованием таких свойств UI Automation, как AutomationID, шаблоны элементов управления и слушатели событий. Пример, использующий Microsoft UI Automation для создания тестовых скриптов, основанных на взаимодействии пользователя с user interface (UI), см. в разделе Test Script Generator Sample.
        '''--------------------------------------------------------------------
        ''' <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 'CreateUIAThread


        '''--------------------------------------------------------------------
        ''' <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 'CreateUIAWorker

        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 thtat 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 'Playback


'''--------------------------------------------------------------------
''' <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 'WriteToScript
        ///--------------------------------------------------------------------
        /// <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>
/// 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 thtat 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);
}

Использование относительного пути для возврата к ранее идентифицированному AutomationElement

  • В некоторых случаях, поскольку AutomationID гарантированно уникален только среди родственных элементов одного уровня, несколько элементов в дереве модели автоматизации пользовательского интерфейса могут иметь идентичные значения свойств AutomationID. В этих случаях элементы могут быть уникальным образом идентифицированы по родителю и при необходимости по прародителю. К примеру, разработчик может создать строку меню с несколькими элементами меню, у каждого из которых есть несколько дочерних элементов меню, где дочерние узлы идентифицируются с помощью последовательных идентификаторов AutomationID, например, "Item1", "Item2" и т. д. Каждый элемент меню может затем быть однозначно идентифицирован по его идентификатору AutomationID вместе с идентификатором AutomationID его родителя и при необходимости вместе с идентификатором AutomationID его прародителя.

См. также

Задачи

Нахождение Элемента Автоматизации Пользовательского Интерфейса в Зависимости от Состояния Свойства

Ссылки

AutomationIdProperty

Основные понятия

Общие сведения о дереве модели автоматизации пользовательского интерфейса