MBeckius
MBeckius

Reputation: 1527

Load html with WebBrowser Control while Blocking UI Thread

I have a .Net 4.5 WinForms application that contains html stored in a database. The user is presented with the list of pages. As they click on a row the page is loaded into the WebBrowser. I want the process of loading the page to be synchronous with the main UI thread (IOW, I don't want the user to be able to do anything else until the page is loaded). The code for loading the html is:

public class DocLoader
{
    private readonly WebBrowser browser;
    readonly TaskCompletionSource<object> loadedTCS = new TaskCompletionSource<object>();

    public DocLoader(WebBrowser browser)
    {
        this.browser = browser;
    }

    private void LoadedComplete(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        Debug.WriteLine("doc completed");
        loadedTCS.TrySetResult(null);
        browser.DocumentCompleted -= LoadedComplete;
    }

    public Task LoadDoc(string html)
    {
        browser.DocumentCompleted += LoadedComplete;
        browser.DocumentText = html;
        return loadedTCS.Task;
    }
}

Calling the code like this:

await new DocLoader(webBrowser1).LoadDoc(html);

Will result in the message loop being able to sill process messages. This is undesirable. For example the user could click on another document in the list before the previous has completed loading.

Calling the code like this:

new DocLoader(webBrowser1).LoadDoc(html).Wait();

Results in the UI freezing and the document never loading, presumably because the DocumentCompleted event will not fire w/o the message loop running.

Is there anyway to accomplish this task and keep the process synchronous with the UI Thread?

Upvotes: 1

Views: 981

Answers (1)

noseratio
noseratio

Reputation: 61666

The WebBrowser requires its parent thread to pump message to operate properly. In your case, its parent thread is the main UI thread, so you cannot block it with Wait. One hack might be to disable the whole UI, as browser.DocumentText asynchronous loading is reasonably fast:

Cursor.Current = Cursors.WaitCursor;
mainFrom.Enabled = false;
try
{
    await new DocLoader(webBrowser1).LoadDoc(html);
}
finally
{
    mainFrom.Enabled = true;
    Cursor.Current = Cursors.Default;
}

This however is quite user-unfriendly. A proper approach would be to support cancellation in your WebBrowser asynchronous loading scenario. You'd need to cancel the pending operation if the user clicks another row (while the the previous row update is still pending), and start a new one.

Upvotes: 1

Related Questions