JoeR
JoeR

Reputation: 1911

Waiting for WebBrowser ajax content

I want to pause the execution of my thread until a particular div has been loaded via ajax into a WebBrowser instance. Obviously I can continuously check for the presence of this div doing something like:

while (Browser.Document.GetElementById("divid") == null) { Thread.Sleep(200); }

However, sleeping the thread that the Browser is in between loops only blocks the browser from actually loading the content in the first place. It seems, therefore, that I need to execute the Browser.Navigate method in a separate thread - I can then continue to check/wait for the presence of the div whilst the WebBrowser instance continues loading the URL I asked it to.

My attempts at this, however, have failed and I'd value any input on how I should go about this. I thought simply dispatching a new thread with new Thread(() => { Browser.Navigate(url); }); would work but after doing so, nothing loads and the Browser.ReadyState remains as 'Uninitialized'. I presume I'm misunderstanding how to go about properly threading procedures like this with C# and would value some advice!

Upvotes: 11

Views: 11278

Answers (4)

Duncan Lukkenaer
Duncan Lukkenaer

Reputation: 13924

You should not call Thread.Sleep as it will block the UI thread.

A better solution is to create an asynchronous task. Inside this task you can call Task.Delay which won't interfere with the UI.

static async Task<IHTMLElement> WaitForElement(WebBrowser browser, string elementID, TimeSpan timeout)
{
    long startTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
    var timeoutMS = timeout.TotalMilliseconds;

    // While the timeout has not passed...
    while (DateTimeOffset.Now.ToUnixTimeMilliseconds() - startTime < timeoutMS)
    {
        // Wait for 200ms
        await Task.Delay(TimeSpan.FromMilliseconds(200));

        // Check if the document contains the element
        var document = (HTMLDocument) browser.Document;
        var element = document.getElementById(elementID);
        if (element != null)
        {
            // Element found, stop looping
            return element;
        }
    }

    throw new Exception($"Element was not loaded after {timeoutMS} milliseconds");
}

The code above checks the DOM every 200 milliseconds to see if the element with the given ID exists. It also contains a timeout (e.g. 10 seconds) in case the element never gets loaded for any unexpected reason.

Here is an example showing how to use this function to read the value out of a text field added to the document after an AJAX call:

var input = (IHTMLInputElement) await WaitForElement(myBrowserControl, "input-id", TimeSpan.FromSeconds(10));
var value = input.value; // Read the value of the input field

Upvotes: 0

Ponson
Ponson

Reputation: 87

The following should work,

while (Browser.Document.GetElementById("divid") == null) 
{ 
    Application.DoEvents();
    Thread.Sleep(200); 
}

The above worked for me...

Upvotes: 7

Federico Aineseder
Federico Aineseder

Reputation: 77

You can find a text into body for example:

while (HtmlWindow.Document.Body.InnerHtml.Contains(some_text) == false)
{        
    Application.DoEvents();
    Thread.Sleep(200);
}

Upvotes: 0

Sheng Jiang 蒋晟
Sheng Jiang 蒋晟

Reputation: 15261

Don't block the main thread's message pump. Since the browser is an STA component, xmlhttprequest won't be able to raise events from the background thread if you block the message pump. You can't navigate in a background thread. The Windows Forms wrapper of the webbrowser ActiveX does not support access from other threads than the UI thread. Use a timer instead.

Upvotes: 6

Related Questions