0

How can I create System.Windows.Forms.WebBrowser in background STA thread? I try use some code like this:

var tr = new Thread(wbThread);
tr.SetApartmentState(ApartmentState.STA);
tr.Start();

private void wbThread()
{
     CWebBrowser browser = new CWebBrowser();
     var text = browser.Navigate("http://site.com", CWebBrowser.EventType.loadCompleted).Body.InnerHtml;
}

CWebBrowser - custom class, wich delegate System.Windows.Forms.WebBrowser object Navigate method and wait until page completed loads. The problem is LoadCompleted event on System.Windows.Forms.WebBrowser object never raises. I found some solution here, but it does not work (can't find method Application.Run() on my WPF app).

public class CWebBrowser : ContentControl
{
    public readonly System.Windows.Forms.WebBrowser innerWebBrowser;
    private readonly AutoResetEvent loadCompletedEvent;
    private readonly AutoResetEvent navigatedEvent;

    public enum EventType
    {
        navigated, loadCompleted
    }

    public CWebBrowser()
    {
        innerWebBrowser = new System.Windows.Forms.WebBrowser();
        loadCompletedEvent = new AutoResetEvent(false);
        navigatedEvent = new AutoResetEvent(false);

        System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
        host.Child = innerWebBrowser;
        Content = host;

        innerWebBrowser.DocumentCompleted +=new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(innerWebBrowser_DocumentCompleted);
        innerWebBrowser.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(innerWebBrowser_Navigated);
    }

    void innerWebBrowser_Navigated(object sender, System.Windows.Forms.WebBrowserNavigatedEventArgs e)
    {
        navigatedEvent.Set();
    }

    void innerWebBrowser_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
    {
        if (((sender as System.Windows.Forms.WebBrowser).ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete) || innerWebBrowser.IsBusy)
            return;

        var doc = innerWebBrowser.Document;
        loadCompletedEvent.Set();
    }

    public System.Windows.Forms.HtmlDocument Navigate(string url, EventType etype)
    {
        if (etype == EventType.loadCompleted)
            loadCompletedEvent.Reset();
        else if (etype == EventType.navigated)
            navigatedEvent.Reset();

        innerWebBrowser.Navigate(url);

        if (etype == EventType.loadCompleted)
            loadCompletedEvent.WaitOne();
        else if (etype == EventType.navigated)
            navigatedEvent.WaitOne();

        System.Windows.Forms.HtmlDocument doc = null;
        Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(
            delegate
            {
                doc = innerWebBrowser.Document;
            }));

        return doc;
    }
}

Thansk for all advices and sorry for my bad english :o(

Community
  • 1
  • 1
Dmitriy
  • 654
  • 10
  • 24
  • 1
    You are breaking the STA contract, the thread must pump a message loop. http://stackoverflow.com/questions/4269800/webbrowser-control-in-a-new-thread/4271581#4271581 – Hans Passant Dec 27 '11 at 15:34
  • Why are you using the com based Form? The WPF control is System.Windows.Controls.WebBrowser – paparazzo Dec 27 '11 at 16:08
  • What is the purpose of this code? Are you doing all this just to try and the html output from a web site? – paparazzo Dec 27 '11 at 16:11
  • I use winforms WebBrowser, because I want use ReadyState property. And I use webbrowser for downloading pages, executing a lot of javascripts. – Dmitriy Dec 27 '11 at 19:44
  • Hans Passant, could you comment your post? (post a sample) Thanks – Dmitriy Dec 27 '11 at 19:46
  • So the purpose of the code is to get the rendered html after all the scripts have processed? – paparazzo Dec 27 '11 at 20:56

1 Answers1

0

Why don't you use the default WebBrowser control like this?

public MainPage()
{
    InitializeComponent();

    System.Windows.Deployment.Current.Dispatcher.BeginInvoke(startNavigate);
}

void startNavigate()
{
    WebBrowser wb = new WebBrowser();
    wb.LoadCompleted += new LoadCompletedEventHandler(wb_LoadCompleted);
    wb.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(wb_Navigated);
    wb.Navigate(new Uri("http://www.google.com"));
}

void wb_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    // e.Content
}

void wb_LoadCompleted(object sender, NavigationEventArgs e)
{
    // e.Content when the document finished loading.
}

Edit: You are using old System.Windows.Forms.WebBrowser control, instead System.Windows.Controls.WebBrowser which is part of WPF.

Tomislav Markovski
  • 12,331
  • 7
  • 50
  • 72
  • Try run your code yourself - it doesn't work :( (event never fire) – Dmitriy Dec 27 '11 at 15:06
  • I edited my post to use `Dispatcher.BeginInvoke`. And yes, the navigate event is fired. I just tried it. – Tomislav Markovski Dec 27 '11 at 15:09
  • But is it true, that in your edited solution WebBrowser control created in main dispatcher thread? Big pages with a lot of javascript will be render at this thread and application will freezed? – Dmitriy Dec 27 '11 at 15:12
  • `BeginInvoke` runs asynchronously, so it's very safe - will not freeze your application. – Tomislav Markovski Dec 27 '11 at 15:14
  • But I'm also need wait for complete loading and rendering page after it has been navigated. If I will use BeginInvoke() it was freeze my app. Can you run your solution with my CWebBrowser object? App starts freeze at WaitOne() method :(. – Dmitriy Dec 27 '11 at 15:26
  • See my edit. I added `LoadCompelted` event. Also, you are using old `System.Windows.Forms.WebBrowser`, instead `System.Windows.Controls.WebBrowser` which is WPF control. – Tomislav Markovski Dec 27 '11 at 15:36
  • I use old webbrowser, because I want use ReadyState property. But in your last solution LoadCompleted event never raised too. Do you try it yourselft? (sorry for my question :(). – Dmitriy Dec 27 '11 at 15:41
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/6161/discussion-between-dmitriy-and-tomislav-markovski) – Dmitriy Dec 27 '11 at 15:58
  • hi. im trying this as well, `loadcompleted` is never fired. any solutions fellows? – Yisroel M. Olewski Dec 11 '12 at 08:41
  • Bringing this thread back to life, I have followed instructions here and found that sometimes my loadcompleted event does not fire. This seems to happen when the browser is not a child of something. When I make it a child of the Grid it locks my application up. If I try to make it a child of a grid in the LoadCompleted event it doesn't get added as the event does not fire. The LoadCompleted event also does not fire when the control is added to the Grid in the Navigated event. Does anyone have any other ideas? – Nigel Ellis Mar 26 '14 at 11:16