Använda egenskapen AutomationID
Kommentar
Den här dokumentationen System.Windows.Automation är avsedd för .NET Framework-utvecklare som vill använda de hanterade UI Automation-klasserna som definierats i namnområdet. Den senaste informationen om UI Automation finns i Windows Automation API: UI Automation.
Det här avsnittet innehåller scenarier och exempelkod som visar hur och när AutomationIdProperty kan användas för att hitta ett element i UI Automation-trädet.
AutomationIdProperty identifierar unikt ett UI Automation-element från dess syskon. Mer information om egenskapsidentifierare som rör kontrollidentifiering finns i Översikt över egenskaper för användargränssnittsautomatisering.
Kommentar
AutomationIdProperty garanterar inte en unik identitet i hela trädet. Den behöver vanligtvis container- och omfångsinformation för att vara användbar. Ett program kan till exempel innehålla en menykontroll med flera menyalternativ på den översta nivån som i sin tur har flera underordnade menyalternativ. Dessa sekundära menyalternativ kan identifieras med ett allmänt schema som "Item1", "Item 2" och så vidare, vilket tillåter duplicerade identifierare för underordnade över menyalternativ på den översta nivån.
Scenarier
Tre primära UI Automation-klientprogramscenarier har identifierats som kräver användning av AutomationIdProperty för att uppnå korrekta och konsekventa resultat när du söker efter element.
Kommentar
AutomationIdProperty stöds av alla UI Automation-element i kontrollvyn förutom programfönster på den översta nivån, gränssnittsautomatiseringselement som härleds från WPF-kontroller (Windows Presentation Foundation) som inte har något ID eller x:Uid och UI Automation-element som härleds från Win32-kontroller som inte har något kontroll-ID.
Använd ett unikt och identifierbart AutomationID för att hitta ett specifikt element i UI Automation-trädet
- Använd ett verktyg som UI Spy för att rapportera AutomationIdProperty om ett UI-element av intresse. Det här värdet kan sedan kopieras och klistras in i ett klientprogram, till exempel ett testskript för efterföljande automatiserad testning. Den här metoden minskar och förenklar den kod som krävs för att identifiera och hitta ett element vid körning.
Varning
I allmänhet bör du försöka få endast direkta underordnade RootElementtill . En sökning efter underordnade kan iterera genom hundratals eller till och med tusentals element, vilket kan resultera i ett stackspill. Om du försöker hämta ett visst element på en lägre nivå bör du starta sökningen från programfönstret eller från en container på en lägre nivå.
///--------------------------------------------------------------------
/// <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
Använd en beständig sökväg för att återgå till ett tidigare identifierat AutomationElement
- Klientprogram, från enkla testskript till robusta post- och uppspelningsverktyg, kan kräva åtkomst till element som för närvarande inte instansieras, till exempel en filöppningsdialogruta eller ett menyalternativ och därför inte finns i UI Automation-trädet. Dessa element kan bara instansieras genom att återskapa eller "spela upp", en specifik sekvens av användargränssnittsåtgärder med hjälp av UI Automation-egenskaper som AutomationID, kontrollmönster och händelselyssnare.
///--------------------------------------------------------------------
/// <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
Använd en relativ sökväg för att återgå till ett tidigare identifierat AutomationElement
- I vissa fall kan flera element i UI Automation-trädet ha identiska AutomationID-egenskapsvärden eftersom AutomationID endast garanteras vara unikt bland syskon. I dessa situationer kan elementen identifieras unikt baserat på en överordnad och, om det behövs, en mor- och farförälder. En utvecklare kan till exempel tillhandahålla en menyrad med flera menyalternativ var och en med flera underordnade menyalternativ där underordnade objekt identifieras med sekventiella AutomationID:er, till exempel "Item1", "Item2" och så vidare. Varje menyalternativ kan sedan identifieras unikt av dess AutomationID tillsammans med AutomationID för dess överordnade och, om det behövs, dess mor- och farförälder.