Does White support UI Automation for Infragistics controls ?

Dec 16, 2008 at 11:18 AM

Hi All,

We are using Infragistics NetAdvantage 6.3 version to develop our Window Forms application.


Now I'm trying to write test code using White UI Automation.
 


Our application has more Infragistics controls. But I'm not sure whether white supports this.

When I checked, I could work on some basic controls like UltraTextEditor, UltraButton etc but not for the controls like UltraGrid, UltraTree.


Please help me if anybody know about this . . . Thank You !

Thanks & Regards,
Kannan

Dec 18, 2008 at 8:06 AM
Hi,

I think you need to check whether Infragistics supports UIAutomation for the mentioned controls - UltraGrid, UltraTree or not.
To do that you can use UISpy -from what I read- if UISpy finds the grid and its elements, you will be able to use White to automate the user actions you want to perform on that control.
That is because White uses UI Automation API to fnd controls on a window.


I hope this helps
Dec 24, 2008 at 3:55 PM

Hi,

Thanks for the reply.

Infragistics doesn't support Microsot UI Automation.

But I could do using white code itself.

White (tables/rows/columns) is coded based on the Microsoft DataGrid control.

So I've modified the white code and could access the rows and columns.

Thanks & Regards,
Kannan

Jan 5, 2009 at 1:10 PM
Hi,

It sounds very intresting what you said about modifying white code.
Would it be a problem for you to share some details on what modifications have you done on the code?

It would be greatly appreciated.

Thanks
Kamlesh
Feb 22, 2009 at 10:09 AM

Hi Kamlesh,

 

I’ve done the following changes,

1.       I think that the current White implementation for table is written for DataGridView.

DataGridView has a row called “Top Row” which contains the header information.

But there is no such row for UltraGrid on Infragistics.

So I took the first row to get the column info.

 

This is done in Table.Header property as,

 

     // "Top Row" for DataGridView and "row 1" for UltraGrid . . .

     Condition condition ;

 

     if (this.Name == "DataGridView")

     {

            condition = SearchCriteria.ByText("Top Row").AutomationCondition;

     }

     else if (this.AutomationElement.Current.AutomationId == "ultraGrid")

     {

            condition = SearchCriteria.ByText("row 1").AutomationCondition;

     }

 

     header = (TableHeader)new TableHeaderFactory().Create(finder.Descendant(condition), actionListener);

 

                If the data table of the data source (which was set for the UltraGrid) has name, the the condition should be,

 

                                condition = SearchCriteria.ByText(TableName + " row 1").AutomationCondition; // This can be verified in UISpy

 

                In the above I’ve hard coded the ultra grid name and the table name. Need to find a generic way to do this . . .

 

2.       Then the columns are created like below in TableHeader.Columns property,

 

get

      {

          // AutomationElementCollection descendants =

          //    new AutomationElementFinder(automationElement).Descendants(AutomationSearchCondition.ByControlType(ControlType.Header));

          //List<AutomationElement> columnElements =

          //    new BricksCollection<AutomationElement>(descendants).FindAll(

          //        delegate(AutomationElement obj) { return !obj.Current.Name.StartsWith("Row "); });

          //return new TableColumns(columnElements, actionListener);

 

if (columns != null)

            {

                  return columns;

            }

 

            //

            // Here the automationElement is the row which is returned from the Table.Header

      // So from the children of this automation element, columns are costructed . . .

      //

            List<AutomationElement> columnElements = new List<AutomationElement>();

 

            AutomationElementCollection automationElements = automationElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));

 

            foreach (AutomationElement element in automationElements)

            {

                  columnElements.Add(element);

            }

 

            columns = new TableColumns(columnElements, actionListener);

 

            return columns;

 }

 

3.       Then the rows are retrieved by the following code,

 

  static TableRowFactory()

        {

            rowPredicate =

                delegate(AutomationElement element)

                {

                    // return element.Current.Name.ToLower().StartsWith("row ".ToLower()) && element.Current.Name.Split(' ').Length == 2;

                    return element.Current.Name.ToLower().Contains("row ".ToLower());

                };

        }

 

                The above modifications is enough to get the rows, but I’ve made the following changes in the GetRowElements method to improve performance,

 

                     private List<AutomationElement> GetRowElements()

        {

            #region CUSTOM_CODE

 

            //

            // Following changes are done to increase the performance because the

      //  first commented line is taking long time . . .

            //

 

            //AutomationElementCollection descendants = automationElementFinder.Descendants(AutomationSearchCondition.ByControlType(ControlType.Custom));

            //BricksCollection<AutomationElement> automationElements = new BricksCollection<AutomationElement>(descendants);

 

            //return automationElements.FindAll(rowPredicate);

 

            List<AutomationElement> rowElements = new List<AutomationElement>();

            AutomationElementCollection rowElementsAE = automationElementFinder.AutomationElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));

 

            foreach (AutomationElement element in rowElementsAE)

            {

                if (element.Current.Name.ToLower().Contains("row ".ToLower()))

                {

                    rowElements.Add(element);

                }

            }

 

            #endregion

 

            return rowElements;

        }

4.       Then the following changes are done in TableCellFactory.CreateCells method,

 

public virtual TableCells CreateCells(TableHeader tableHeader, AutomationElement rowElement)

        {

            #region CUSTOM_CODE

 

            if (this.tableElement.Current.Name == "DataGridView")

            {

                if (customControlTypes == null)

                {

                    customControlTypes = new AutomationElementFinder(tableElement).Descendants(AutomationSearchCondition.ByControlType(ControlType.Custom));

                }

                Predicate<AutomationElement> cellPredicate;

 

                int zeroBasedRowNumber = int.Parse(S.LastWords(rowElement.Current.Name, 1)[0]);

 

                cellPredicate = delegate(AutomationElement element)

                                {

                                    string name = element.Current.Name;

                                    return name.ToLower().Contains(" Row ".ToLower()) && zeroBasedRowNumber == int.Parse(S.LastWords(name, 2)[1]);

                                };

 

                List<AutomationElement> tableCellElements = new BricksCollection<AutomationElement>(customControlTypes).FindAll(cellPredicate);

                return new TableCells(tableCellElements, tableHeader, actionListener);

            }

            else if (this.tableElement.Current.AutomationId == "ultraGrid")

            {

                //cellPredicate = delegate(AutomationElement element)

                //                {

                //                    string name = element.Current.Name;

 

                //                    foreach (UIItem column in tableHeader.Columns)

                //                    {

                //                        if (name.Contains(column.Name))

                //                        {

                //                            AutomationElement child = rowElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.RuntimeIdProperty, element.GetRuntimeId()));

 

                //                            return child != null;

                //                        }

                //                    }

 

                //                    return false;

                //                };

 

                //

                // The above coomented works fine . . .

    // But it is commented and the following lines are added just to increase the performance . . .

                //

                AutomationElementCollection children = rowElement.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom));

 

                List<AutomationElement> tableCellElements = new List<AutomationElement>();

 

                foreach (AutomationElement child in children)

                {

                    tableCellElements.Add(child);

                }

 

                return new TableCells(tableCellElements, tableHeader, actionListener);

            }

            else

            {

                throw new System.Exception("Unknown table type.");

            }

 

            #endregion

 }

 

Please try this. If you need any clarifications, please let me know. Really very sorry for very late reply :-(

 

Thanks & Regards,

Kannan

Coordinator
Feb 28, 2009 at 4:33 PM
I would recommend using CustomUIItem for doing this because incase you have dot net datagridview and this control at the same time you would not be able to use both.
the documentation for this is there on the website for CustomUIItem
Mar 3, 2009 at 9:42 AM
Just like KannanThirumal I use the same components and have the same problems.

It's was great that the White framework give support to some commercial components like DevExpress and Infragists.

The creation of a sample for this grid with CustomUIItem it will be appreciated by the community. I'm sure of that.


Jun 2, 2009 at 12:35 PM

I am also using infragistics grid and I want to test it. I tried KannanThirumal code but it could not run. Does anybody have any solution to test infragistic grid?

Thanks in advance.

Jun 2, 2009 at 12:50 PM

Hi,

 

I’m able to access the rows and cells using the above custom code I mentioned.

 

If you have any problem, please let me know. Will try to address.

 

Also if you have access to source code, you can implement Server-Side UI Automation Provider.

For sample, please check the example in the following link,

 

http://msdn.microsoft.com/en-us/library/ms771502.aspx

 

Thanks & Regards,

Kannan

Jun 9, 2009 at 7:05 PM

Which is the Infragistics version that you are using?

The White framework don't support DevExpress, but I can automate my applciation in a similar way that you presented, but with Infragistics when I try to see the Automation properties throught UISpy my applciation block. The same when I use White.Core to find the grid.

 

I think that my be a problem even worse with the version that I'm using, and I could check If the upgrade is not so hard.

Jun 10, 2009 at 7:43 AM

Hi,

 

I’m using version Infragistics 6.3.

 

Thanks & Regards,

Kannan

Jun 19, 2009 at 5:59 PM

Hi,

Thanks for reply. To solve this problem I decided to waste a little time creating a custom control for infragistics grid.

This class as already some usefull function: Like to get the Rows, Headers, to put the cell value, get cell value based on position. As I will need more functions I will add to here.

This also means that to get the grid we need just to do the followong

// Gets the control that represents the grid.DataPresenterCustomControl dataPresenter = parentWindow.Get<DataPresenterCustomControl>(SearchCriteria.ByAutomationId("GRID_AUTOMATIONID"));

Hope this help.

p.s. My version of infragistic is 7.2, this means that in newer version could need some adjustments.

using System.Windows.Automation;
using Core.AutomationElementSearch;
using Core.UIItems.Actions;
using Core.UIItems.Custom;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Primavera.Athena.QualityTools.Automation.CustomControls
{
    /// <summary>
    /// Custom class that represents the table: DevExpress.
    /// </summary>
    [ControlTypeMapping(CustomUIItemType.Custom)]
    public class DataPresenterCustomControl : CustomUIItem
    {

        #region Members

        /// <summary>
        /// Initializes a new instance of the <see cref="DataPresenterCustomControl"/> class.
        /// </summary>
        /// <param name="automationElement">The automation element.</param>
        /// <param name="actionListener">The action listener.</param>
        public DataPresenterCustomControl(AutomationElement automationElement, ActionListener actionListener)
            : base(automationElement, actionListener)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataPresenterCustomControl"/> class.
        /// </summary>
        protected DataPresenterCustomControl() { }

        #endregion

        #region Public Methods

        /// <summary>
        /// Sets the value on a cell.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <param name="value">The value.</param>
        /// <returns>Return a element that represents the cell.</returns>
        public AutomationElement CellValue(int row, int column, string value)
        {
            // Create a new subset to find the items header elements
            AutomationElementCollection tableDataItemCells =
                new AutomationElementFinder(this.Rows()[row])
                .Children(AutomationSearchCondition.ByControlType(ControlType.Custom));
            Assert.IsTrue(tableDataItemCells.Count > 0, "Failed to find table header items: AutomationSearchCondition.ByControlType(ControlType.HeaderItem)");

            // Gets the value pattern
            ValuePattern valuePattern = UIElementPattern.GetValuePattern(tableDataItemCells[column]);
            Assert.IsNotNull(valuePattern, "Failed to find row value pattern.");

            // Sets value on cell
            valuePattern.SetValue(value);

            return tableDataItemCells[column];
        }

        /// <summary>
        /// Sets the value on a cell.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <returns>
        /// Return a element that represents the cell.
        /// </returns>
        public AutomationElement Cell(int row, int column)
        {
            // Create a new subset to find the items header elements
            AutomationElementCollection tableDataItemCells =
                new AutomationElementFinder(this.Rows()[row])
                .Children(AutomationSearchCondition.ByControlType(ControlType.Custom));
            Assert.IsTrue(tableDataItemCells.Count > 0, "Failed to find table header items: AutomationSearchCondition.ByControlType(ControlType.HeaderItem)");

            return tableDataItemCells[column];
        }

        /// <summary>
        /// Returns the value of a cell.
        /// </summary>
        /// <param name="row">The row.</param>
        /// <param name="column">The column.</param>
        /// <returns>Return the value of a cell.</returns>
        public string CellValue(int row, int column)
        {
            // Create a new subset to find the items header elements
            AutomationElementCollection tableDataItemCells =
                new AutomationElementFinder(this.Rows()[row])
                .Children(AutomationSearchCondition.ByControlType(ControlType.Custom));
            Assert.IsTrue(tableDataItemCells.Count > 0, "Failed to find table header items: AutomationSearchCondition.ByControlType(ControlType.HeaderItem)");

            // Gets the value pattern
            ValuePattern valuePattern = UIElementPattern.GetValuePattern(tableDataItemCells[column]);
            Assert.IsNotNull(valuePattern, "Failed to find row value pattern.");

            // returns the value on cell
            return valuePattern.Current.Value.ToString();
        }

        /// <summary>
        /// Gets the header.
        /// </summary>
        /// <returns>
        /// Returns a automation collection elements.
        /// </returns>
        public AutomationElementCollection Header()
        {
            // Create a new subset to find the header element
            AutomationElement tableHeader =
                new AutomationElementFinder(this.AutomationElement)
                .Descendant(AutomationSearchCondition.ByControlType(ControlType.Header));
            Assert.IsNotNull(tableHeader, "Failed to find table header: AutomationSearchCondition.ByControlType(ControlType.Header)");

            // Create a new subset to find the items header elements
            AutomationElementCollection tableHeaderItems =
                new AutomationElementFinder(tableHeader)
                .Children(AutomationSearchCondition.ByControlType(ControlType.HeaderItem));
            Assert.IsNotNull(tableHeaderItems, "Failed to find table header items: AutomationSearchCondition.ByControlType(ControlType.HeaderItem)");

            return tableHeaderItems;
        }

        /// <summary>
        /// Returns all the rows of the table.
        /// </summary>
        /// <returns> Returns all the rows of the table.</returns>
        public AutomationElementCollection Rows()
        {
            // Create a new subset to find all rows on grid

            AutomationElementCollection tableDataRows =
                new AutomationElementFinder(this.AutomationElement)
                .Descendant(AutomationSearchCondition.ByClassName("RecordListControl"))
                .FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "Record"));
            Assert.IsTrue(tableDataRows.Count > 0, "Failed to find table data items: AutomationSearchCondition.ByControlType(ControlType.DataItem)");

            return tableDataRows;
        }

        /// <summary>
        /// Gets the column position.
        /// </summary>
        /// <param name="tableHeaderItems">The table header items.</param>
        /// <param name="columnName">Name of the column.</param>
        /// <returns>
        /// Returns the column position based on its name.
        /// </returns>
        public int ColumnPosition(AutomationElementCollection tableHeaderItems, string columnName)
        {
            int columnNumber = -1;
            bool columnFound = false;

            foreach (AutomationElement automationElement in tableHeaderItems)
            {
                columnNumber++;
                if (automationElement.Current.Name == columnName)
                {
                    columnFound = true;
                    break;
                }
            }

            return columnFound ? columnNumber : -1;
        }

        #endregion
    }
}

 

Nov 16, 2009 at 5:37 AM
Edited Nov 16, 2009 at 5:38 AM

hi

 

I didn't find "UIElementPattern" in your code .Which dll i can add to access this.

 

Thanks.

Giri.D