Użyj właściwości AutomationID
Uwaga
Ta dokumentacja jest przeznaczona dla deweloperów programu .NET Framework, którzy chcą używać zarządzanych klas automatyzacja interfejsu użytkownika zdefiniowanych w System.Windows.Automation przestrzeni nazw. Aby uzyskać najnowsze informacje na temat automatyzacja interfejsu użytkownika, zobacz Interfejs API usługi Windows Automation: automatyzacja interfejsu użytkownika.
Ten temat zawiera scenariusze i przykładowy kod pokazujący, jak i kiedy AutomationIdProperty można go użyć do zlokalizowania elementu w drzewie automatyzacja interfejsu użytkownika.
AutomationIdPropertyunikatowo identyfikuje element automatyzacja interfejsu użytkownika z jego elementów równorzędnych. Aby uzyskać więcej informacji na temat identyfikatorów właściwości związanych z identyfikacją kontrolek, zobacz automatyzacja interfejsu użytkownika Właściwości — omówienie.
Uwaga
AutomationIdProperty nie gwarantuje unikatowej tożsamości w całym drzewie; zwykle wymaga, aby informacje o kontenerze i zakresie były przydatne. Na przykład aplikacja może zawierać kontrolkę menu z wieloma elementami menu najwyższego poziomu, które z kolei mają wiele elementów menu podrzędnego. Te pomocnicze elementy menu mogą być identyfikowane przez schemat ogólny, taki jak "Item1", "Item 2" itd., umożliwiając zduplikowane identyfikatory dla elementów menu najwyższego poziomu.
Scenariusze
Zidentyfikowano trzy podstawowe scenariusze aplikacji klienckiej automatyzacja interfejsu użytkownika, które wymagają użycia AutomationIdProperty w celu uzyskania dokładnych i spójnych wyników podczas wyszukiwania elementów.
Uwaga
AutomationIdPropertyjest obsługiwany przez wszystkie elementy automatyzacja interfejsu użytkownika w widoku sterowania z wyjątkiem okien aplikacji najwyższego poziomu, automatyzacja interfejsu użytkownika elementów pochodzących z kontrolek Windows Presentation Foundation (WPF), które nie mają identyfikatora lub x:Uid i automatyzacja interfejsu użytkownika elementów pochodzących z kontrolek Win32, które nie mają identyfikatora kontrolki.
Użyj unikatowego i odnajdowalnego identyfikatora AutomationID, aby zlokalizować określony element w drzewie automatyzacja interfejsu użytkownika
- Użyj narzędzia, takiego jak UI Spy, aby zgłosić AutomationIdProperty interesujący go element interfejsu użytkownika. Tę wartość można następnie skopiować i wkleić do aplikacji klienckiej, na przykład skrypt testowy na potrzeby kolejnych testów automatycznych. Takie podejście zmniejsza i upraszcza kod niezbędny do identyfikowania i lokalizowania elementu w czasie wykonywania.
Uwaga
Ogólnie rzecz biorąc, należy spróbować uzyskać tylko bezpośrednie dzieci obiektu RootElement. Wyszukiwanie elementów potomnych może iterować przez setki lub nawet tysiące elementów, co może spowodować przepełnienie stosu. Jeśli próbujesz uzyskać określony element na niższym poziomie, należy rozpocząć wyszukiwanie w oknie aplikacji lub z kontenera na niższym poziomie.
///--------------------------------------------------------------------
/// <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
Użyj trwałej ścieżki, aby powrócić do wcześniej zidentyfikowanego elementu AutomationElement
- Aplikacje klienckie, od prostych skryptów testowych do niezawodnych narzędzi do rejestrowania i odtwarzania, mogą wymagać dostępu do elementów, które nie są obecnie tworzone, takich jak okno dialogowe otwierania pliku lub element menu, a zatem nie istnieją w drzewie automatyzacja interfejsu użytkownika. Te elementy mogą być tworzone tylko przez odtworzenie lub "odtwarzanie" konkretnej sekwencji akcji interfejsu użytkownika za pomocą właściwości automatyzacja interfejsu użytkownika, takich jak AutomationID, wzorce kontrolek i odbiorniki zdarzeń.
///--------------------------------------------------------------------
/// <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
Użyj ścieżki względnej, aby powrócić do wcześniej zidentyfikowanego elementu AutomationElement
- W pewnych okolicznościach, ponieważ identyfikator AutomationID jest gwarantowany tylko jako unikatowy wśród elementów równorzędnych, wiele elementów w drzewie automatyzacja interfejsu użytkownika może mieć identyczne wartości właściwości AutomationID. W takich sytuacjach elementy można jednoznacznie zidentyfikować na podstawie rodzica i, w razie potrzeby, dziadka. Na przykład deweloper może podać pasek menu z wieloma elementami menu z wieloma elementami menu podrzędnego, w których elementy podrzędne są identyfikowane z sekwencyjnymi identyfikatorami AutomationID, takimi jak "Item1", "Item2" itd. Każdy element menu może być następnie jednoznacznie zidentyfikowany przez jego identyfikator AutomationID wraz z identyfikatorem AutomationID jego elementu nadrzędnego i, w razie potrzeby, jego dziadkami.