This article shows how to make use of UI Automation Framework using WPF to develop an UI Automated application. The UI Automation Library is included in the .NET Framework 3.0/3.5. The concept seems to be straightforward. Every UI control is an "AutomationElement
" which can be found through searching from the Parent "AutomationElement
". The top AutomationElement
is defined as the "RootElement
", which is the desktop.
Another way to get the AutomationElement
Object to a given Window is by starting a "Process
", and then using the Process MainWindowHandle
to get the AutomationElement
object.
I am sharing information on this framework because I found it very interesting and had a very hard time developing a full featured application. On the internet, I found a very small amount or I must say not adequate amount of material. So I thought of writing some code-sample to help others and may find useful. Here, I am not going to explain what is UI Automation Framework and its details. You can find a theoretical article on internet/MSDN. Here I will provide how to make use of the UI Automation libraries, and how to play with this framework using WinForm controls.
I was given the scenario, where there is already an application developed in WinForms using .NET Framework 2.0. And UI Automation application is developed in WPF using .NET Framework 3.5. The sample application gives you an idea of how to write UI Automation application using UI Automation Framework, i.e., how to use UI Automation Class Libraries, how to find controls and write automated test cases.
The solution contains 2 projects, one project is WinForms application targeting .NET Framework 2.0 and the second project is WPF application targeting .NET Framework 3.5. The WPF application is basically a test that invokes WinForms application, which has some controls like Textbox
, dropdown
, checkbox
, grid
, etc. I showed singleTestCase
execution on a single button click, but once you get the idea, you can play with it and write a series of test cases that can be executed in a single action.
Note: When you compile this solution, I set output path for WinForm application to bin folder of WPF application just to make it easier to understand and load the application.
Automation.RootElement: It represents the desktop, and all other applications on desktop appear as child windows of this RootElement
. The below code shows how to find the target instance of the application from the desktop.
private void LoadApplication_Click(object sender, RoutedEventArgs e) { //Start the client application this._clientProcess = Process.Start (this._targetApplicationPath + @"\" + this._targetApplicationName); Thread.Sleep(100); int count = 0; do { ++count; //Get the Root Element of client application from the Desktop tree this._clientAppRootInstance = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "SampleForm")); Thread.Sleep(100); } while (this._clientAppRootInstance == null && count < 10); if (this._clientAppRootInstance == null) { MessageBox.Show("SampleForm application instance not found. Close application and try again!!!"); return; } }
AutomationElement
: This object exposes common properties of the UI elements they represent. One of these properties is the control type, which defines its basic appearance and functionality as a single recognizable entity (e.g., a button or check box). In addition, elements expose control patterns that provide properties specific to their control types. Control patterns also expose methods that enable clients to get further information about the element and to provide input.TreeScope
: It represents enumeration which has values to specify the scope of element within the UI Automation tree or in Root element.PropertyCondition
: It represents the condition that tests whether property has a specified value.AndCondition
: It represents the combination of 2 or more conditions that must all be true
for a particular match.private void TextBoxTest_Click(object sender, RoutedEventArgs e) { if (this._clientAppRootInstance != null) { //Get the textbox (appears as Document Control Type) instance from //application root instance as it will child //and add the AndCondition so minimize the search time in tree textBoxInstance = this._clientAppRootInstance.FindFirst(TreeScope.Children, new AndCondition( new PropertyCondition (AutomationElement.AutomationIdProperty, "txtDocument"), new PropertyCondition (AutomationElement.ControlTypeProperty, ControlType.Document) )); if (textBoxInstance == null) { MessageBox.Show("Textbox instance not found"); } else { //Set focus to textbox textBoxInstance.SetFocus(); //Use SendKeys to send text System.Windows.Forms.SendKeys.SendWait(_tempText); } } }
UI Automation supports different types of patterns depending upon the controls. Below is the code sample ofTextPattern
.
private void CompareText_Click(object sender, RoutedEventArgs e) { //Control that accept the values as represented as TextPattern TextPattern textboxPattern = (TextPattern)textBoxInstance.GetCurrentPattern(TextPattern.Pattern); if (textboxPattern == null) { MessageBox.Show("Textbox instance not found."); } else { string enteredText = textboxPattern.DocumentRange.GetText(-1).Trim(); if (string.Equals(enteredText, _tempText, StringComparison.InvariantCultureIgnoreCase)) { MessageBox.Show("Compare test passed"); } else { MessageBox.Show("Compare test failed."); } } }
Below is the code sample that shows how to select value from combobox
. Combobox
supportsExpandCollapsePattern
and SelectionItemPattern
. Combobox
contains List
as children and List
contains the collection of ListItems
in combobox
.
private void ComboBoxTest_Click(object sender, RoutedEventArgs e) { if (this._clientAppRootInstance != null) { //Get the combo box instance from application root instance as its //child instance and find by its name AutomationElement comboboxInstance = this._clientAppRootInstance.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "cmbMonth")); if (comboboxInstance == null) { MessageBox.Show("ComboBox instance not found."); } else { //Get the List child control inside the combo box AutomationElement comboboxList = comboboxInstance.FindFirst(TreeScope.Children, new PropertyCondition (AutomationElement.ControlTypeProperty, ControlType.List)); //Get the all the listitems in List control AutomationElementCollection comboboxItem = comboboxList.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem)); //Combo Box support 2 patterns, ExpandCollapsePatterna and SelectionItemPattern //Expand the combobox ExpandCollapsePattern expandPattern = (ExpandCollapsePattern)comboboxInstance.GetCurrentPattern (ExpandCollapsePattern.Pattern); expandPattern.Expand(); //Index to set in combo box AutomationElement itemToSelect = comboboxItem[comboboxItem.Count - 7]; //Finding the pattern which need to select Object selectPattern = null; if (itemToSelect.TryGetCurrentPattern (SelectionItemPattern.Pattern, out selectPattern)) { ((SelectionItemPattern)selectPattern).Select(); } } } }
Button
supports InvokePattern
, which represents the control that initiates or performs a single action. Like code executes on button click, it does not maintain the state when activated.
private void AddDataRowTest_Click(object sender, RoutedEventArgs e) { if (this._clientAppRootInstance != null) { //Get the Add Button instance from root instance, finding it by name //and its child in root instance AutomationElement buttonAddInstance = this._clientAppRootInstance.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "btnAdd")); if (buttonAddInstance == null) { MessageBox.Show("Add button instance not found"); } else { //Button support Invoke Pattern InvokePattern buttonPattern = (InvokePattern)buttonAddInstance.GetCurrentPattern(InvokePattern.Pattern); //Once get the pattern then calling Invoke method on that buttonPattern.Invoke(); } } }
The code sample below shows how to create and attach an event handler on checkbox
in the target application:
private void CheckBoxTest_Click(object sender, RoutedEventArgs e) { if (this._clientAppRootInstance != null) { //Get the checkbox instance from root application instance and //it appears as child in the tree and finding by its name AutomationElement checkBoxInstance = this._clientAppRootInstance.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "chkCheckBox")); if (checkBoxInstance == null) { MessageBox.Show("Checkbox instance not found"); } else { //Create EventHandler for CheckBox AutomationPropertyChangedEventHandler checkBoxEvent = new AutomationPropertyChangedEventHandler(CheckBoxIsChecked_EventHandler); //Attaching the EventHandler Automation.AddAutomationPropertyChangedEventHandler(_clientAppRootInstance, TreeScope.Children, checkBoxEvent, AutomationElement.NameProperty); } } }
Below is the Event Handler code which executes when user checks the checkbox on target application:
private void CheckBoxIsChecked_EventHandler(object sender, AutomationEventArgs e) { //If CheckBox is checked on target application then this get executed AutomationElement ar = sender as AutomationElement; MessgeBox.Show("Checkbox IsChecked event executed."); }
I hope this post is useful to others. Please rate this article and provide your valuable feedback.