huge data table finding rows

Aug 18, 2011 at 10:10 AM
Edited Aug 18, 2011 at 10:20 AM

I am a newer in White , do you guys know how to find a row in huge data table(in win32 form) ,and  the table has more than 500 records. 

Here is my codes

 

        public class GeographicalRegionGridClass
        {
            const string Description_COLUMN_NAME = "Description";
            const string ACTIVE_COLUMN_NAME = "Active";

            private Window _window;
            private Table _table;

            public GeographicalRegionGridClass(Window window)
            {
                _window = window;
                _table = _window.Get(SearchCriteria.ByAutomationId("dataGridView1"));
            }

            public int Get_row_count_by_name_and_active(string name, bool active)
            {
                var rowCount = 0;
                foreach (var row in _table.Rows)
                {
                    var cells = row.Cells;
                    if (cells[Description_COLUMN_NAME].Value.ToString() == name
                        && Convert.ToBoolean(cells[ACTIVE_COLUMN_NAME].Value) == active)
                    {
                        rowCount++;
                    }
                }

                return rowCount;
            }

            public void Click_row_by_name(string name)
            {
                _table.FindAll(Description_COLUMN_NAME, name)[0].Click();
            }

            public void Refresh()
            {
                _table.Refresh();
            }
        }


 

and it cost me a long time to find the row , even to find the first row , is there any other methods to resolve it ? 

Thanks

Coordinator
Aug 19, 2011 at 12:49 PM

http://white.codeplex.com/wikipage?title=Performance&referringTitle=Home

Aug 19, 2011 at 2:03 PM

I have also been experiencing slow performance when automating a DatagridView (Table).  I've tried all of the performance tips on the wiki above, but to no performance improvement.  The problem, as identified on other discussion posts, seems to be with the Table.Rows (getter) which loads the the entire grid.  This is taking a significant amount of time (in my experience, over 5 minutes for a 20 row, 10 column grid).  Ideally, the grid should be lazy-loaded.  For example, calling Table.Rows.Count, would return with the number of rows without loading them.  Then if I do, Table.Rows[10], just row 10 is loaded.  Is this something that would be possible?

Regards

Matthew

 

 

Coordinator
Aug 30, 2011 at 1:33 PM

Can you post a sample code which is based on controlling search depth which I can look at.

http://white.codeplex.com/wikipage?title=Search%20depth

Oct 24, 2011 at 2:40 PM
Edited Oct 24, 2011 at 2:41 PM

I've managed to get the the Rows getter to work faster by using a depth count of 1.  However when I try using the same technique on the Cells getter (to click on a specific cell) I run into 1 of 2 problems.

1) If I use a depth count of 1, the cells are not loaded and I get: System.ArgumentOutOfRangeException : Index was out of range.

2) If I use a depth count of 2,  it's taking over 1 minute to find the 1 cell out of 400.

Here's the test code I've been using.  I created a small winforms app with a DataGridView containing 20 rows x 20 columns to automate.

        [Test]
        public void ShouldFindAndClickCellInUnder5Seconds()
        {
            var app = Application.Launch("WhiteTableTest.exe");
            try
            {
                var form = app.GetWindow(SearchCriteria.ByAutomationId("DataGridViewWhiteTestForm"), InitializeOption.NoCache);
                var grid = form.Get<Table>("TestDataGridView");
                var start = DateTime.Now;
                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
                CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 2;
                grid.Rows[10].Cells[2].Click();
                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = false; 
                var end = DateTime.Now;
                Assert.Less(end.TimeOfDay.Subtract(start.TimeOfDay), new TimeSpan(0, 0, 0, 5));
            }
            finally
            {
                app.Kill();  
            }
        }

This is how I create the DataTable (DataSet) to bind to the DataGridView:

    public class DataHelpers
    {
        public const int Rows = 20;
        private const int COLUMNS = 20;

        public static DataTable BuildDataTable()
        {
            var table = new DataTable("Data");
            // create columns
            for (var colIdx = 1; colIdx <= COLUMNS; colIdx++)
            {
                table.Columns.Add("col" + colIdx, typeof(string));
            }
            //create rows
            for (var rowIdx = 0; rowIdx < Rows; rowIdx++)
            {
                var rowData = new List<string>();

                // create row data 
                for (var valIdx = 1; valIdx <= COLUMNS; valIdx++)
                {
                    rowData.Add("Value : " + (((rowIdx) * COLUMNS) + valIdx));
                }
                table.Rows.Add(rowData.ToArray());
            }
            return table;
        }

    }

Regards

Matthew

Coordinator
Nov 1, 2011 at 2:18 PM

have you tried something like:

CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 1;
Row row = grid.Rows[10];
CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 2;
row.Cells[2].Click();
CoreAppXmlConfiguration.Instance.RawElementBasedSearch = false;
Nov 3, 2011 at 2:21 PM
Edited Nov 3, 2011 at 2:23 PM

I did try this but get a NullReferenceException when accessing Cells collection (in the line: row.Cells[2].Click(); below). 

The grid.Rows[10] call returns a TableRow object but without the nested cell info, presumably because the initial MaxElementSearchDepth was set to 1.

        [Test]
        public void ShouldFindAndClickCellInUnder5Seconds()
        {
            var app = Application.Launch("WhiteTableTest.exe");
            try
            {
                var form = app.GetWindow(SearchCriteria.ByAutomationId("DataGridViewWhiteTestForm"), InitializeOption.NoCache);
                var grid = form.Get<Table>("TestDataGridView");
                var start = DateTime.Now;

                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
                CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 1; 

                TableRow row = grid.Rows[10]; 

                CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 2; 
                
                row.Cells[2].Click();
                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = false;

                var end = DateTime.Now;
                Assert.Less(end.TimeOfDay.Subtract(start.TimeOfDay), new TimeSpan(0, 0, 1, 0));
            }
            finally
            {
                app.Kill();  
            }
        }

Nov 4, 2011 at 6:48 AM

Hi mateobozza,

is your code the solution for the problem or shows the problem?

 

Throndorin

 

 

Nov 4, 2011 at 10:02 AM

Hi Throndorin,

The code shows the problem.

Regards

Matthew

Nov 4, 2011 at 12:14 PM
Edited Nov 4, 2011 at 12:16 PM

OK

try the following workaround: (0.21)

 

BricksCollection coll = new BricksCollection(row.AutomationElement.FindAll( TreeScope.Descendants, new PropertyCondition( AutomationElement.ClassNameProperty, "TableCell" )));
TableCells cells = new TableCells( coll, table.ActionListener, table.Header ); cells[2].Click();

 

Throndorin

Nov 4, 2011 at 2:34 PM

I've just tried this, but it gives an 'ArgumentOutOfRangeException : Index was out of range' exception when I execute cells[2].  The row.AutomationElement.FindAll call returns an empty collection. I've tried this with and without using RawElementBasedSearch to find the row.

Here's the code I tried:

        [Test]
        public void ShouldFindAndClickCellInUnder5Seconds()
        {
            var app = Application.Launch("WhiteTableTest.exe");
            try
            {
                var form = app.GetWindow(SearchCriteria.ByAutomationId("DataGridViewWhiteTestForm"), InitializeOption.NoCache);
                var grid = form.Get<Table>("TestDataGridView");
                var start = DateTime.Now;

                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
                CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 1; 

                TableRow row = grid.Rows[10]; 

                var coll = new BricksCollection<TableCell>(row.AutomationElement.FindAll(TreeScope.Descendants, new PropertyCondition(AutomationElement.ClassNameProperty, "TableCell")));
                TableCells cells = new TableCells(coll, grid.Header, grid.ActionListener); 
                cells[2].Click();

                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = false;
                var end = DateTime.Now;
                Assert.Less(end.TimeOfDay.Subtract(start.TimeOfDay), new TimeSpan(0, 0, 1, 0));
            }
            finally
            {
                app.Kill();  
            }
        }
Nov 7, 2011 at 7:22 AM
Edited Nov 7, 2011 at 7:23 AM

Hi

is the cell in view, if you call the find operation?

we have ha d a similiar problem finding rows in datagrids. I write an operation which scrolls completly down and touch all rows and cells => this is time-consuming but works form me.

 

Throndorin

Nov 14, 2011 at 4:38 PM

Hi Throndorin,

Yes, the cell that I'm trying to access in the test is visible.  My form shows 16 Rows and 5 columns and my test tries to access row 10, column 2.

Regards

Matthew

Nov 15, 2011 at 6:46 AM

Hi

have you tried to wait before getting the cells, does the problem also appear if you debug the test?

Is the classname for cells correct? Is the cell custom ?

If it is possible,  post LogStructure for the table here.

 

Throndorin

 

Nov 15, 2011 at 4:14 PM

Hello again,

To clarify, the problem I'm reporting is with performance, rather than not being able to get a cell.  Using the standard white api (i.e not using CoreAppXmlConfiguration.Instance.MaxElementSearchDepth), I can find all my rows and cells, but it takes a long time (i.e minutes, even for small tables). 

To speed my searches with grids, Vivek suggested that I use CoreAppXmlConfiguration.Instance.MaxElementSearchDepth.  This works well with finding rows quickly, but doesn't seem to work with then finding cells quickly within a row.  The problem appears to be that when you do a search using MaxElementSearchDepth = 1 to get the rows quicly, the collection of rows that you get returned doesn't have any child object depth itself.  I'm guessing this is because the collection is a copy of the original TableRow objects without any child (cell) information included.  Maybe if the collection contained links to the actual TableRow objects, rather than copies of the TableRow objects, I would then be able to use MaxElementSearchDepth = 2 to quickly find a cell within a TableRow object.

Regards,

Matthew

 

 

Dec 7, 2011 at 11:01 AM

Follow-up, I've now got this to work in under 5 seconds :) Here's the code:

        [Test]
        public void ShouldFindAndDoubleClickCellInUnder5Seconds()
        {
            var app = Application.Launch("WhiteTableTest.exe");
            try
            {
                var rowToClick = 10;
                var colToClick = "col2";

                var form = app.GetWindow(SearchCriteria.ByAutomationId("DataGridViewWhiteTestForm"), InitializeOption.NoCache);
                var grid = form.Get<Table>("TestDataGridView");

                var start = DateTime.Now;
                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = true;
                CoreAppXmlConfiguration.Instance.MaxElementSearchDepth = 1;
                TableRow row = grid.Rows[rowToClick];
                CoreAppXmlConfiguration.Instance.RawElementBasedSearch = false;
                var end = DateTime.Now;
                Assert.Less(end.TimeOfDay.Subtract(start.TimeOfDay), new TimeSpan(0, 0, 0, 3), "Time taken to find rows");

                start = DateTime.Now;
                var cellName = string.Format("{0} {1}", colToClick, row.Name);
                var coll = new BricksCollection<AutomationElement>(row.AutomationElement.FindAll(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, cellName)));
                TableCells cells = new TableCells(coll, grid.Header, grid.ActionListener);
                TableCell cell = cells[0];
                end = DateTime.Now;
                Assert.Less(end.TimeOfDay.Subtract(start.TimeOfDay), new TimeSpan(0, 0, 0, 2), "Time taken to find column");

                cell.DoubleClick();
            }
            finally
            {
                app.Kill();  
            }
        }

Notes: 

  • Used MaxElementSearchDepth to quicly get the rows in the grid.
  • Using LogStructure() I noticed that the ClassName property was not being populated on the cell automation elements.  So I used the Name propery instead to quickly find the single cell automation element that I wanted to double click. 
  • For real-world use, I've wrapped the above code in an extension methods for the Table and TableRow classes and added some extra validation.
  • When I reported before that I was getting NullReferenceException, it turns out it was because I was using Click() rather than DoubleClick() rather than the Cells collection being un-initialised.  Not sure why I cannot do a single click on a cell.

Thanks to Vivek and Throndorin for your suggestions that finally lead me to the answer :)