Utilizzare la proprietà AutomationID
![]() |
---|
La presente documentazione è destinata agli sviluppatori di .NET Framework che desiderano utilizzare le classi UI Automation gestite definite nello spazio dei nomi System.Windows.Automation.Per informazioni aggiornate sull'UI Automation, vedere Windows Automation API: Automazione interfaccia utente (la pagina potrebbe essere in inglese). |
Questo argomento contiene scenari e codice di esempio in cui viene illustrato come e quando è possibile utilizzare la proprietà AutomationIdProperty per individuare un elemento all'interno della struttura ad albero di UI Automation.
AutomationIdProperty identifica in modo univoco un elemento di automazione interfaccia utente dagli elementi di pari livello. Per ulteriori informazioni sugli identificatori di proprietà correlati all'identificazione di controlli, vedere Cenni preliminari sulle proprietà di automazione interfaccia utente.
![]() |
---|
AutomationIdProperty non garantisce un'identità univoca in tutta la struttura ad albero. Per essere utile, richiede informazioni sul contenitore 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", "Item2" e così via", consentendo identificatori duplicati per le voci figlio tra le voci di menu di livello superiore. |
Scenari
Sono stati identificati tre scenari principali di applicazioni client di automazione interfaccia utente che richiedono l'utilizzo di AutomationIdProperty per ottenere risultati accurati e coerenti durante la ricerca di elementi.
![]() |
---|
AutomationIdProperty è supportato da tutti gli elementi di automazione interfaccia utente nella visualizzazione controlli, eccetto le finestre di applicazione di primo livello, gli elementi di automazione interfaccia utente derivati da controlli di Windows Presentation Foundation (WPF) non associati a un ID o x:Uid e gli elementi di automazione interfaccia utente derivati da controlli di Win32 non associati a un ID controllo. |
Utilizzare un elemento AutomationID univoco e individuabile per individuare un elemento specifico nella struttura ad albero di automazione interfaccia utente
- Utilizzare uno strumento, ad esempio UI Spy, per segnalare la proprietà AutomationIdProperty di un elemento dell'UI di interesse. Questo valore può quindi essere copiato e incollato in un'applicazione client, ad esempio uno script di test per i successivi test automatizzati. Questo approccio riduce e semplifica il codice necessario per identificare e individuare un elemento in fase di esecuzione.
![]() |
---|
In generale, è necessario tentare di ottenere solo elementi figlio diretti della proprietà RootElement.Una ricerca dei discendenti potrebbe ripetersi in centinaia o addirittura migliaia di elementi, con la possibilità di un overflow dello stack.Se si tenta di ottenere un elemento specifico a un livello inferiore, è necessario 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 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));
}
Utilizzare un percorso persistente per tornare a un elemento AutomationElement identificato in precedenza
- Le applicazioni client, dai semplici script di test a utility complete di registrazione e riproduzione, potrebbero richiedere l'accesso a elementi di cui non è stata attualmente creata un'istanza, ad esempio una finestra di dialogo di apertura file o una voce di menu, e che pertanto non esistono nella struttura ad albero di automazione interfaccia utente. È possibile creare istanze di questi elementi solo riproducendo una sequenza specifica di azioni dell'user interface (UI) tramite l'utilizzo delle proprietà di UI Automation, ad esempio AutomationID, pattern di controllo e listener di eventi. Per un esempio in cui viene utilizzata l'Microsoft UI Automation per generare script di test basati sull'interazione utente con l'user interface (UI), vedere 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);
}
Utilizzare un percorso relativo per tornare a un elemento AutomationElement identificato in precedenza
- In determinate circostanze, poiché la proprietà AutomationID è garantita per essere univoca solo tra elementi di pari livello, è possibile che più elementi nella struttura ad albero di automazione interfaccia utente abbiano valori identici di questa proprietà. In tali situazioni gli elementi possono essere identificati in modo univoco in base a un elemento padre e, se necessario, un elemento padre del padre. Ad esempio, uno sviluppatore può fornire una barra dei menu con più voci di menu, ognuna con più voci di menu figlio, in cui gli elementi figlio sono identificati con AutomationID sequenziali, quali "Item1", "Item2" e così via. Ogni voce di menu potrebbe quindi essere identificata in modo univoco dalla relativa proprietà AutomationID oltre che da quella dell'elemento padre e, se necessario, da quella dell'elemento padre del padre.
Vedere anche
Attività
Trovare un elemento di automazione interfaccia utente in base a una proprietà
Riferimenti
Concetti
Cenni preliminari sulla struttura ad albero di automazione dell'interfaccia utente