Search code examples
wpfvisualtreehelper

What causes VisualTreeHelper::HitTest to return incomplete results, and sometimes no results?


I had a scenario where I was connecting to a Windows WPF app, through a network connection to drive UI for the purposes of test automation. Sometimes my requests to the UI using VisualTreeHelper returned no results regardless of the apparent state of the logical or visual trees.

This, more than a question is the documentation of the solution to the problem.

It turns out that I was detecting the network connection availability and IMMEDIATELY after it responded, I issued calls that exercised VisualTreeHelper and catching the UI while being rendered. The result of that call was incomplete and subsequent calls failed completely (no elements were found ever).

My practical solution was to delay for 2 seconds between when the communication was open, and the first call to VisualTreeHelper. That eliminated the weird state caused by calling UI too soon. I tested with 100 iterations of the app rebooting with no problems.

I 'Googled' the problem but didn't find good results.

Interestingly, I asked ChatGPT about it and it informed me: "If you try to access elements of the visual tree before they have been fully rendered, you may get unexpected results, or an exception may be thrown."

I asked the bot to tell me what sources it had for the assertion and the only link I got: "here's a link to the Microsoft documentation that mentions the risk of calling VisualTreeHelper before the visual tree is fully rendered:

https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.visualtreehelper.getchild?view=netcore-3.1#remarks"

Unfortunately such link actually doesn't mention anything like that. This may be due to ChatGPT using 2021 data, and the link content has changed.

A friend suggested I used archive.log (an Internet archive) to see if a previous version of such link had the desired information, but none was found.

ChatGPT provided relevant information, but could not provide sources where it distilled it from.

Lastly, I acknoledge that the 'ideal' way to address the problem of racing to use VisualTreeHelper before the UI is done rendering is to wait for an event the main window calls: private void MainWindow_Loaded(object sender, RoutedEventArgs e); and gate any use of VisualTreeHelper until this event is raised.

My approach is not stricktly the ideal, but practical with little effort. The 2 second delay does not significantly impact my scenario.

After I delayed 2 seconds before using VisualTreeHelper to wait for UI to render, I had no problems.


Solution

  • The short answer is (as you have observed) that they're not there yet when you try finding the controls.

    The visual tree that the visual tree helper works with is the object graph of UI controls etc which are within a window.

    A window is a content control.

    When you instantiate a window, the window itself is created using the constructor.

    I'm not sure there's a point where there can be just the window itself in the visual tree but this is conceptually how it works.

    Parent windows is first then the root panel content and the content of that root.

    The "tree" of controls is window > content panel > child controls.

    Constructing a window can take a while. 2 seconds seems kind of slow but developers are prone to putting all sorts of things in constructors. The time taken may vary depending on how much code the developer has put in there, connectivity speeds etc etc.

    That variance would explain why a test result will vary.

    The correct event to handle for a window would be contentrendered. As the name suggests, this fires after all the content of the window has been rendered.

    With a usercontrol or page you can handle loaded but you're best deferring processing via use of dispatcher. Loaded doesn't guarantee everything is rendered.