Silverlight - Retrieving TextBock.Text property is slow

18 posts, 0 answers
  1. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 06 Apr 2012 Link to this post

    I am in the process of improving the performance of our automation suite against a browser based silverlight application.

    One of the major areas that I am unable to work around is getting the Text property of a TextBlock control that is nested inside a data grid.  On average it takes 3/4 of a second to get the value.  This is done thousands of times when the full automation suite is run and it ends up adding 20 - 30 minutes to our test suite just to get a value from a TextBlock control.

    The following code is an example of how I am timing reading the TextBlock.Text Property

    public string GetCellValue()
    {
        string value;
        var textBlock = TableCell.Find.ByType<TextBlock>();
                 
        var sw = new System.Diagnostics.Stopwatch();
        sw.Start();
                 
        value = textBlock.Text;  //Takes on average 750 milliseconds
                 
        sw.Stop();
        Tracer.CaptureTiming( "GetCellValue", sw.Elapsed.Ticks );  //Capturing timing
     
        return value;
    }

    The TextBlock does not have any nested elements.  Only text.

    Is there a more performant way to get the text property of a TextBlock control?  I could really use some advice on this.

    Here are some specs of my environment:
    WebAII version: Telerik_Testing_Framework_2011_2_1413_FREE_EDITION
    OS: Win7 64 bit
    Browser: IE 9 32 bit
    Test Harness: Nunit
    .Net Version: 4.0
    Visual Studio Version: 2010 Pro
    Ram: 6gb
    CPU: 2 Cores @ 2.1 Ghz
  2. Anthony
    Admin
    Anthony avatar
    19 posts

    Posted 10 Apr 2012 Link to this post

    Hello Aaron,

    Here's the code I tested this with against a Microsoft Silverlight site:

    Manager.Settings.Web.EnableSilverlight = true;
    Manager.LaunchNewBrowser(BrowserType.InternetExplorer);
     
    SilverlightApp app = ActiveBrowser.SilverlightApps()[0];
    TreeView treeView = app.Find.ByName<TreeView>("SampleSelection");
    Assert.IsNotNull(treeView);
     
    TreeViewItem tvi = app.Find.ByExpression(new XamlFindExpression("XamlTag=stackpanel", "name=SampleSelectionContainer", "|", "XamlPath=/treeviewitem[1]/grid[0]/itemspresenter[name=ItemsHost]/stackpanel[0]/treeviewitem[0]")).As<TreeViewItem>();
    tvi.User.Click();
     
    System.Threading.Thread.Sleep(2000);
    app.RefreshVisualTrees();
     
    DataGrid grid = app.Find.ByAutomationId<DataGrid>("dataGrid");
    Assert.IsNotNull(grid);
     
    DataGridRow row = grid.Rows[2];
    DataGridCell firstName = row.Cells[2];
     
    var textBlock = firstName.Find.ByType<TextBlock>();
     
    var sw = new System.Diagnostics.Stopwatch();
    sw.Start();
     
    string value = textBlock.Text;
     
    sw.Stop();
     
    Log.WriteLine("textBlock.Text: " + value);
    Log.WriteLine("sw Elapsed Ticks: " + sw.Elapsed.Ticks.ToString());
    Log.WriteLine("sw Elapsed msec: " + sw.Elapsed.Milliseconds.ToString());
     
    var sw2 = new System.Diagnostics.Stopwatch();
    sw2.Start();
     
    string value2 = firstName.TextBlockContent;
     
    sw2.Stop();
     
    Log.WriteLine("cell.TextBlockContent: " + value2);
    Log.WriteLine("sw2 Elapsed Ticks: " + sw2.Elapsed.Ticks.ToString());
    Log.WriteLine("sw2 Elapsed msec: " + sw2.Elapsed.Milliseconds.ToString());

    Notice the two methods I'm using to get a specific cell's textblock content. Here is the log output for that code:

    [Trace] : textBlock.Text: Joe
    [Trace] : sw Elapsed Ticks: 281683
    [Trace] : sw Elapsed msec: 28
    [Trace] : cell.TextBlockContent: Joe
    [Trace] : sw2 Elapsed Ticks: 220578
    [Trace] : sw2 Elapsed msec: 22

    Notice the second method is slightly faster. I'd also like to confirm you aren't misinterpreting the elapsed ticks value. In the second example above, 220,578 ticks equals just 22 milliseconds.

    Do you experience a long retrieval time against the public site that I used? If not, is the issue specific to your application? If so I'd like to test it directly and reproduce the issue.

    All the best,
    Anthony
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  3. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 10 Apr 2012 Link to this post

    Hi Anthony,

    Thank you for the reply.  As far as I can tell the slowness appears to be specific to my application.

    Here are the results that I get when running your test in my environment:

    textBlock.Text: Joe
    sw Elapsed Ticks: 642246
    sw Elapsed msec: 64.2246
    cell.TextBlockContent: Joe
    sw2 Elapsed Ticks: 516076
    sw2 Elapsed msec: 51.6076

    As you can see my hardware isn't a powerful as yours but it's still close enough to your values but pretty far away from what I am seeing when executing similar logic against my application.

    As far as my timings go I am aware that ticks don't translate to milliseconds directly.  When reporting on the execution times they get converted correctly back to timespan objects.

    Here are the results of a single test that hits a data grid pretty hard in our application:
    Name               Count   Total               Average             Max             
    FullTest           1       00:06:10.4903458    00:06:10.4903458    00:06:10.4903458
    GetCellValue       295     00:04:40.2030161    00:00:00.9498407    00:00:01.9915330

    Note that I only have the results for the FullTest execution time and the GetCellValue function.  I have timings around other locations but have removed those results.

    In this case you can see that we are spending a signification amount more time in getting the TextBlock.Value than when running your test.  

    FYI, I actually started with Cell.TextBlockContent when attempting to troubleshoot this and I end up with close to the same results.

    As far as testing directly goes how do you normally go about that?

    Thanks,
    Aaron
  4. Anthony
    Admin
    Anthony avatar
    19 posts

    Posted 11 Apr 2012 Link to this post

    Hello Aaron,

    Typically a user will provide a public URL for their app and detailed instructions on how to reproduce the issue. Ideally you'd supply a full test I can run directly.

    If you consider that area of your app too sensitive for a public forum, consider providing a public site that demonstrates the same issue, or ask your developers to craft a small sample portion of your app.

    Regards,
    Anthony
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  5. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 11 Apr 2012 Link to this post

    Ok.  I am working on putting something together that you can review.
  6. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 18 Apr 2012 Link to this post

    I am not able to post on a public forum, however, I am able to privately submit a portion of the compiled application and the source code for the test suite.

    Is there a way I can privately submit a zip file to you?
  7. Anthony
    Admin
    Anthony avatar
    19 posts

    Posted 19 Apr 2012 Link to this post

    Hello Aaron,

    You can attach it to an email to support@telerik.com. Be sure to reference number 531484.

    Regards,
    Anthony
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  8. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 20 Apr 2012 Link to this post

    Hello Anthony,

    I have submitted the code to the support email address.  Have you received it?
    Please let me know if there is anything else you need.

    Thank You,
    Aaron
  9. Cody
    Admin
    Cody avatar
    3354 posts

    Posted 20 Apr 2012 Link to this post

    Hi Aaron,

    Yes we did receive it thank you. We are studying it now. We'll report our findings shortly.

    All the best,
    Cody
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  10. Cody
    Admin
    Cody avatar
    3354 posts

    Posted 20 Apr 2012 Link to this post

    Hi Aaron,

    After a thorough analysis I have concluded there's something wrong with your code accessing and/or timing the access of the text block. To prove this I recreated the same approach in a Test Studio test using a coded step to access and scan the rows. My results show an average access time of a mere 24 milliseconds as demonstrated in this video. Here is the log from my test run:

    Overall Result: Pass
    ------------------------------------------------------------
    '4/20/2012 5:25:26 PM' - Using .Net Runtime version: '4.0.30319.239' for tests execution.
    '4/20/2012 5:25:26 PM' - Starting execution....
    '4/20/2012 5:25:26 PM' - Detected a Silverlight Test. Setting EnableSilverlight=True
    '4/20/2012 5:25:28 PM' - Detected custom code in test. Locating test assembly: MySampleTests.dll.
    '4/20/2012 5:25:28 PM' - Assembly Found: C:\Users\gibson\Documents\Visual Studio 2010\Projects\MySampleTests\MySampleTests\bin\Debug\MySampleTests.dll
    '4/20/2012 5:25:28 PM' - Loading code class: 'MySampleTests.Customer_Tests._531484.WebWiseSilverlightSample'.
    ------------------------------------------------------------
    ------------------------------------------------------------
    '4/20/2012 5:25:28 PM' - Using 'InternetExplorer' version '9.0' as default browser.
    '4/20/2012 5:25:30 PM' - 'Pass' : 1. Navigate to : 'http://localhost:52612/WebWise.Client.SilverlightTestPage.aspx'
    '4/20/2012 5:25:32 PM' - 'Pass' : 2. Type 'Steve' into TextTextbox
    '4/20/2012 5:25:33 PM' - 'Pass' : 3. Click LogonButtonButton
    '4/20/2012 5:25:34 PM' - 'Pass' : 4. Click LogonButtonButton
    '4/20/2012 5:25:35 PM' - 'Pass' : 5. Click ButtonAsyncbutton
    '4/20/2012 5:25:36 PM' - 'Pass' : 6. treeview item 'Report page tests' 'Expand' action.
    '4/20/2012 5:25:38 PM' - 'Pass' : 7. treeview item 'Column' 'Expand' action.
    '4/20/2012 5:25:39 PM' - 'Pass' : 8. Click ButtonAsyncbutton0
    '4/20/2012 5:25:43 PM' - 'Pass' : 9. LeftClick on HeaderTitleTextblock
    '4/20/2012 5:25:45 PM' - 'Pass' : 10. Click NextPageButtonButton
    '4/20/2012 5:25:47 PM' - 'Pass' : 11. Wait for ImageRefreshImage.Opacity 'NotEqual' '1'
    '4/20/2012 5:25:48 PM' - LOG: Verifying data in column: 3
    '4/20/2012 5:25:48 PM' - LOG: Verifying row: 21
    '4/20/2012 5:25:48 PM' - LOG: Verifying row: 22
    '4/20/2012 5:25:48 PM' - LOG: Verifying row: 23
    '4/20/2012 5:25:48 PM' - LOG: Verifying row: 24
    '4/20/2012 5:25:48 PM' - LOG: Verifying row: 25
    '4/20/2012 5:25:48 PM' - LOG: Verifying row: 26
    '4/20/2012 5:25:49 PM' - LOG: Verifying row: 27
    '4/20/2012 5:25:49 PM' - LOG: Verifying row: 28
    '4/20/2012 5:25:49 PM' - LOG: Verifying row: 29
    '4/20/2012 5:25:49 PM' - LOG: Verifying row: 30
    '4/20/2012 5:25:52 PM' - LOG: Verifying row: 1
    '4/20/2012 5:25:52 PM' - LOG: Verifying row: 2
    '4/20/2012 5:25:52 PM' - LOG: Verifying row: 3
    '4/20/2012 5:25:53 PM' - LOG: Verifying row: 4
    '4/20/2012 5:25:53 PM' - LOG: Verifying row: 5
    '4/20/2012 5:25:53 PM' - LOG: Verifying row: 6
    '4/20/2012 5:25:53 PM' - LOG: Verifying row: 7
    '4/20/2012 5:25:53 PM' - LOG: Verifying row: 8
    '4/20/2012 5:25:53 PM' - LOG: Verifying row: 9
    '4/20/2012 5:25:54 PM' - LOG: Verifying row: 10
    '4/20/2012 5:25:54 PM' - LOG: Verifying row: 11
    '4/20/2012 5:25:54 PM' - LOG: Verifying row: 12
    '4/20/2012 5:25:55 PM' - LOG: Verifying row: 13
    '4/20/2012 5:25:55 PM' - LOG: Verifying row: 14
    '4/20/2012 5:25:55 PM' - LOG: Verifying row: 15
    '4/20/2012 5:25:56 PM' - LOG: Verifying row: 16
    '4/20/2012 5:25:56 PM' - LOG: Verifying row: 17
    '4/20/2012 5:25:56 PM' - LOG: Verifying row: 18
    '4/20/2012 5:25:57 PM' - LOG: Verifying row: 19
    '4/20/2012 5:25:57 PM' - LOG: Verifying row: 20
    '4/20/2012 5:25:58 PM' - LOG: Times text read: 30
    '4/20/2012 5:25:58 PM' - LOG: Total text read time: 742 milliseconds
    '4/20/2012 5:25:58 PM' - LOG: Average access time: 24 milliseconds
    '4/20/2012 5:25:58 PM' - 'Pass' : 12. [WebWiseSilverlightSample_CodedStep] : @"New Coded Step
    ------------------------------------------------------------
    '4/20/2012 5:25:58 PM' - Overall Result: Pass
    '4/20/2012 5:25:58 PM' - Duration: [0 min: 29 sec: 814 msec]
    ------------------------------------------------------------
    '4/20/2012 5:25:59 PM' - Test completed!

    Here's the code I used:
    [CodedStep(@"New Coded Step")]
    public void WebWiseSilverlightSample_CodedStep()
    {
        string formatString = "Test - Column {0} - {1:000}";
     
        SilverlightApp app = ActiveBrowser.SilverlightApps()["silverlightControlHost"];
        Assert.IsNotNull(app);
        grid = app.Find.ByAutomationId<DataGrid>("dataGrid1");
        Assert.IsNotNull(grid);
     
        int col = GetColumIndex(grid, "Col02");
        Log.WriteLine("Verifying data in column: " + col.ToString());
     
        for (int row = 21; row <= 30; row++)
        {
            Log.WriteLine("Verifying row: " + row.ToString());
            Assert.AreEqual(string.Format(formatString, 2, row), GetCellValue(row, col), string.Format("Unexpected data in row {0}", row));
        }
     
        app.Find.ByAutomationId<Button>("PreviousPageButton").User.Click();
        grid.Refresh();
     
        for (int row = 1; row <= 20; row++)
        {
            Log.WriteLine("Verifying row: " + row.ToString());
            Assert.AreEqual<string>(string.Format(formatString, 2, row), GetCellValue(row, col), string.Format("Unexpected data in row {0}", row));
        }
     
        Log.WriteLine("Times text read: " + timesAccessed.ToString());
        Log.WriteLine("Total text read time: " + sw.ElapsedMilliseconds.ToString() + " milliseconds");
        Log.WriteLine("Average access time: " + (sw.ElapsedMilliseconds / timesAccessed).ToString() + " milliseconds");
    }
     
    private int GetColumIndex(DataGrid grid, string columnName)
    {
        IList<IDataGridColumnHeader> headers = grid.ColumnHeaderElements;
        for (int i = 0; i < headers.Count; i++)
        {
            if (headers[i].Text.Equals(columnName))
            {
                return i;
            }
        }
        return -1;
    }
     
    private string GetCellValue(int rowToGet, int col)
    {
        // We're going to assume that index 0 of the grid contains the row numbers to scan
        for (int currRow = 0; currRow < grid.RowElements.Count; currRow++)
        {
            if (grid.RowElements[currRow].CellElements[0].Text.Equals(rowToGet.ToString()))
            {
                timesAccessed++;
                sw.Start();
                string value = grid.RowElements[currRow].CellElements[col].Text;
                sw.Stop();
                return value;
            }
        }
        return null;
    }

    The complete test I used is also attached.

     Greetings,
    Cody
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  11. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 24 Apr 2012 Link to this post

    Hi Cody,

    Thank you for responding.  We were able to find that adding calls to Grid.Refresh after paging dramatically improves performance on reading the TextBlock.Text property.

    With that said I was able take your example and rerun it without the call to grid.Refresh and each call to  string value = grid.RowElements[currRow].CellElements[col].Text; went up to 900+ milliseconds after paging.

    Times text read: 30
    Total text read time: 27732 milliseconds
    Average access time: 924 milliseconds

    This is confusing to me.  Based on the name Refresh I would expect to be forced to call it when I am getting stale data from WebAii but not to improve performance when I am getting correct data.

    I could use some information on what Refresh does and when and why I should call it as the documentation does not do an adequate job.

    Also, the call to refresh is an expensive call in and of it self.  2+ seconds when calling on the grid.  What is it doing that takes so long?
  12. Cody
    Admin
    Cody avatar
    3354 posts

    Posted 25 Apr 2012 Link to this post

    Hi Aaron,

    That is a very interesting find regarding Refresh. I looked up our internal code for this... it's fairly trivial what it does:

    /// <summary>
    /// Refresh this FrameworkElement within the VisualTree.
    /// </summary>
    public void Refresh()
    {
        _app.VisualTree.Refresh();
        if (!_app.VisualTree.LastHash.Equals(_lastHash, StringComparison.Ordinal))
        {
            // Re-Find the FrameworkElement.
            FrameworkElement fe;
            if (_findInfo != null)
            {
                // Need to refresh the Find's SearchRoot, unless it is the real root (assuming real root is only one with no parent)
                _findInfo.Find.SearchRoot.Refresh();
                fe = _findInfo.Find.ByExpression(_findInfo.Expression);
            }
            else
            {
                this.Reference.EnsureReferencePath();
                fe = _app.VisualTree.Find.ByReference(this.Reference, true);
            }
     
     
            // Re-assign the automation reference
            this.AssignReference(new AutomationReference(fe));
        }
    }

    Can you send me a test I can run to reproduce this 900+ millisecond timing (one that's much more simplified)? There was so much abstraction in your first test project I was unable to follow it internally.

    Regards,
    Cody
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  13. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 25 Apr 2012 Link to this post

    Hi Cody,

    I don't have Test Studio so I did my best to merge your test into my NUnit fixture.  You can add the following code to the previous test fixture I provided you with and run it through nunit.

    Please note that I have the calls to grid.Refresh() commented out.

    int timesAccessed = 0;
    Stopwatch sw = new Stopwatch();
    DataGrid grid;
             
    Stopwatch refreshStopWatch = new Stopwatch();
     
    [Test]
    public void WebWiseSilverlightSample_CodedStep()
    {
        string formatString = "Test - Column {0} - {1:000}";
     
        string pageTitle = "ColumnTableBasedTest - SqlServer";
        WebWise.Menu.Navigate( "Report Page Automation en-US", "Report page tests", "Column", pageTitle );
        Assert.IsTrue( WebWise.ReportPage.WaitForReportPage( pageTitle ), "Report page was expected." );
     
        WebWise.ReportPage.SortColumn( "Column Table_Based" );
        WebWise.ReportPage.MoveNextPage();
     
        var app = WebWise.SilverlightApp;
        Assert.IsNotNull( app );
                 
     
        grid = app.Find.ByAutomationId<DataGrid>( "dataGrid1" );
                 
        //refreshStopWatch.Start();
        //grid.Refresh(); 
        //refreshStopWatch.Stop();
     
        int col = GetColumIndex( grid, "Col02" );
        Trace.WriteLine( "Verifying data in column: " + col.ToString() );
     
        for ( int row = 21; row <= 30; row++ )
        {
            Trace.WriteLine( "Verifying row: " + row.ToString() );
            Assert.AreEqual( string.Format( formatString, 2, row ), GetCellValue( row, col ), string.Format( "Unexpected data in row {0}", row ) );
        }
     
        app.Find.ByAutomationId<Button>( "PreviousPageButton" ).User.Click();
                 
        //refreshStopWatch.Start();
        //grid.Refresh();
        //refreshStopWatch.Stop();
     
        for ( int row = 1; row <= 20; row++ )
        {
            Trace.WriteLine( "Verifying row: " + row.ToString() );
            Assert.AreEqual( string.Format( formatString, 2, row ), GetCellValue( row, col ), string.Format( "Unexpected data in row {0}", row ) );
        }
     
        Trace.WriteLine( "Times text read: " + timesAccessed.ToString() );
        Trace.WriteLine( "Total text read time: " + sw.ElapsedMilliseconds.ToString() + " milliseconds" );
        Trace.WriteLine( "Average access time: " + ( sw.ElapsedMilliseconds / timesAccessed ).ToString() + " milliseconds" );
     
        //Trace.WriteLine( "Refresh total time: " + refreshStopWatch.ElapsedMilliseconds.ToString() + " milliseconds" );
        //Trace.WriteLine( "Average refresh time: " + ( refreshStopWatch.ElapsedMilliseconds / 2 ).ToString() + " milliseconds" );
    }
     
    private int GetColumIndex( DataGrid grid, string columnName )
    {
        var headers = grid.ColumnHeaderElements;
        for ( int i = 0; i < headers.Count; i++ )
        {
            if ( headers[ i ].Text.Equals( columnName ) )
            {
                return i;
            }
        }
        return -1;
    }
     
    private string GetCellValue( int rowToGet, int col )
    {
        // We're going to assume that index 0 of the grid contains the row numbers to scan
        for ( int currRow = 0; currRow < grid.RowElements.Count; currRow++ )
        {
            if ( grid.RowElements[ currRow ].CellElements[ 0 ].Text.Equals( rowToGet.ToString() ) )
            {
                timesAccessed++;
                sw.Start();
                string value = grid.RowElements[ currRow ].CellElements[ col ].Text;
                sw.Stop();
                return value;
            }
        }
        return null;
    }


    Here are the results of running the test in my environment:

    Without the refresh calls:
    Times text read: 30
    Total text read time: 27042 milliseconds
    Average access time: 901 milliseconds

    With the refresh calls:
    Times text read: 30
    Total text read time: 1523 milliseconds
    Average access time: 50 milliseconds
    Refresh total time: 7881 milliseconds
    Average refresh time: 3940 milliseconds

    Please be mindful that the hardware that I'm running on may not be as powerful as the hardware you are running on.  So you may not see the 900+ milliseconds that I am seeing.  However, I would expect you to see 400+ on your hardware.
  14. Cody
    Admin
    Cody avatar
    3354 posts

    Posted 30 Apr 2012 Link to this post

    Hello Aaron,

    Unfortunately I could not run your code simply because this line complains that SilverlightApp is not defined:
    var app = WebWise.SilverlightApp;

    As an alternative I took my Test Studio test and converted it into a pure Microsoft/Visual Studio coded unit test. I do get similar results when I skip (comment out) the grid.Refresh() line. This line really is needed to that our in-memory copy of the grid is properly refreshed. I'm actually surprised the test works at all without a grid.Refresh.

    Feel free to experiment with the attached project. You should be able to run it in Visual Studio as it. It does not rely on Test Studio... only our free framework.

    Greetings,
    Cody
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  15. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 08 May 2012 Link to this post

    Hi Cody,

    Thank you for your response.

    Sorry about not being able to compile the provided test.  All you'll need to do is make the SilverlightApp property in the WebWiseAutomationClient.cs public.  It should be line 42.  I had made that change locally and forgot to mention it when I posted the test script.

    Also, I was able to run your current sample and see that it does work.  Is there anything I should be doing beyond that?

    In the end, what I am looking for is to improve the overall performance of our automation suite and and the original issue I posted about was the lowest hanging fruit.  Adding a call to refresh does dramatically improve performance of that original scenario.  However, overall, because Refresh is an expensive call it just moves the performance bottleneck to Refresh as I now am calling it more.   

    I can still use some clarification on the purpose of refresh.  Your documentation states the following:

    The VisualTree object exposed by the SilverlightApp is a hierarchical representation of the VisualTree of the Silverlight application at a given point in time. If your Silverlight application changes, you can always refresh this tree by calling VisualTree. Refresh(). The VisualTree also exposes the VisualTree.Root property which is a FrameworkElement that represents the root element of the VisualTree of the application.
    Note: To help eliminate any synchronization issues between the VisualTree available to the test client and the application running in the browser, every time the Find object attempts to find an element in the tree, it will always refresh before starting the search to ensure that the frameworks copy of the tree is in-sync with the application.

    Based on that statement I shouldn't be required to call refresh assuming I re-find the element again.  In my original test script this is what I am doing.  However, It appears that performance degrades significantly when using find to refresh an element.

    When refresh is called on a framework element what is it doing?
    •   - Is it rebuilding the whole visual tree of the whole application?
    •   - Is it rebuilding the visual tree of just that element?
    •   - Is it just clearing the current cache which will be built back up later on?

    Is there anything I can do to improve the performance of Refresh?

    When using find to refresh an element why do subsequent calls to read properties out of that element perform poorly?
    Is there anything I can do to improve performance in this scenario?
  16. Cody
    Admin
    Cody avatar
    3354 posts

    Posted 14 May 2012 Link to this post

    Hello,

    I apologize for the delay responding. I wanted to get some good clear answers out of my software developers first.

    "Is there anything I should be doing beyond that?"

    My intent was to simply demonstrate that I am able to reproduce similar behavior (significant slow down) if the grid.Refresh is commented out.

    I spoke to our developers and they tell me:

    "It looks like FrameworkElement.Refresh() refreshes the FrameworkElement’s containing visual tree – not just the FrameworkElement, but if it’s in a ChildWindow it will only refresh the ChildWindow and not the whole application."

    Could you clarify "When using find to refresh an element ..." i.e. how does Find refresh an element?

    Kind regards,
    Cody
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
  17. Aaron
    Aaron avatar
    12 posts
    Member since:
    Apr 2011

    Posted 17 May 2012 Link to this post

    Hi Cody,

    Thank you again for the response.

      "My intent was to simply demonstrate that I am able to reproduce similar behavior (significant slow down) if the grid.Refresh is commented out."

    Does this mean that you are planning to address it?

    Thank you for the explanation of refresh.  I now understand that calling refresh on a framework element refreshes the visual tree of the whole application.  However it is still unclear what Refresh means.  Does it rebuild the whole visual tree of the application or does it just clear the current cached version?

    Also, is there any way to control the scope of what is getting refreshed? In a way that it will only refresh the visual tree of the element it's called on instead of the whole application?

      Could you clarify "When using find to refresh an element ..." i.e. how does Find refresh an element? 

    Instead of calling grid.Refresh() the documentation implies that I can just "re-find" the element to refresh it since find refreshes the visual tree.


    var grid = app.Find.ByAutomationId<DataGrid>( "dataGrid1" );
      
    int col = GetColumIndex( grid, "Col02" );
    Trace.WriteLine( "Verifying data in column: " + col.ToString() );
      
    for ( int row = 21; row <= 30; row++ )
    {
        Assert.AreEqual( string.Format( formatString, 2, row ), GetCellValue( row, col ), string.Format( "Unexpected data in row {0}", row ) );
    }
      
    app.Find.ByAutomationId<Button>( "PreviousPageButton" ).User.Click();
     
    // Refresh the grid by calling find again
    grid = app.Find.ByAutomationId<DataGrid>( "dataGrid1" );
     
    for ( int row = 1; row <= 20; row++ )
    {
        Assert.AreEqual( string.Format( formatString, 2, row ), GetCellValue( row, col ), string.Format( "Unexpected data in row {0}", row ) );
    }

    The following is the documentation where I am implying that I can do this from:

    To help eliminate any synchronization issues between the VisualTree available to the test client and the application running in the browser, every time the Find object attempts to find an element in the tree, it will always refresh before starting the search to ensure that the frameworks copy of the tree is in-sync with the application.  

  18. Cody
    Admin
    Cody avatar
    3354 posts

    Posted 01 Jun 2012 Link to this post

    Hi,

    I apologize for the delay getting back to you.

    Does this mean that you are planning to address it?

    No because there isn't a problem when the recommended element refresh calls are made. It's only slow when you don't refresh the element i.e. do not follow our recommendations.

    Also, is there any way to control the scope of what is getting refreshed? In a way that it will only refresh the visual tree of the element it's called on instead of the whole application? 

    Sorry but no. In order to refresh an element, the entire Visual Tree needs to be refreshed first, then the portion pertaining to that element is extracted.

    Instead of calling grid.Refresh() the documentation implies that I can just "re-find" the element to refresh it since find refreshes the visual tree.

    Thank you for clarifying. Yes I agree this code will "refresh the grid":

    // Refresh the grid by calling find again
    grid = app.Find.ByAutomationId<DataGrid>( "dataGrid1" );

    Technically it's not "refreshing" but rather it is creating a new grid object, and discarding the old one. Small but important difference.

    Regards,
    Cody
    the Telerik team
    Quickly become an expert in Test Studio, check out our new training sessions!
    Test Studio Trainings
Back to Top