【问题标题】:How to create and use WebBrowser in background thread?如何在后台线程中创建和使用 WebBrowser?
【发布时间】:2012-01-28 13:50:30
【问题描述】:

如何在后台 STA 线程中创建 System.Windows.Forms.WebBrowser?我尝试使用这样的代码:

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 - 自定义类,委托 System.Windows.Forms.WebBrowser 对象导航方法并等待页面完成加载。问题是 System.Windows.Forms.WebBrowser 对象上的 LoadCompleted 事件永远不会引发。我找到了一些解决方案here,但它不起作用(在我的 WPF 应用程序上找不到方法 Application.Run())。

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;
    }
}

感谢所有的建议,对不起我的英语不好:o(

【问题讨论】:

  • 您正在破坏 STA 合同,线程必须泵送消息循环。 stackoverflow.com/questions/4269800/…
  • 为什么使用基于 com 的表单? WPF 控件是 System.Windows.Controls.WebBrowser
  • 这段代码的目的是什么?你做这一切只是为了尝试从网站输出 html 吗?
  • 我使用 winforms WebBrowser,因为我想使用 ReadyState 属性。我使用 webbrowser 下载页面,执行大量 javascripts。
  • Hans Passant,你能评论你的帖子吗? (发布示例)谢谢

标签: wpf multithreading browser


【解决方案1】:

为什么不使用这样的默认 WebBrowser 控件?

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.
}

编辑:您正在使用旧的 System.Windows.Forms.WebBrowser 控件,而不是 System.Windows.Controls.WebBrowser,它是 WPF 的一部分。

【讨论】:

  • 尝试自己运行你的代码 - 它不起作用:((事件永远不会触发)
  • 我编辑了我的帖子以使用Dispatcher.BeginInvoke。是的,导航事件被触发。我刚试过。
  • 但是,在您编辑的解决方案中,在主调度程序线程中创建的 WebBrowser 控件是真的吗?包含大量 javascript 的大页面将在此线程中呈现,应用程序将冻结?
  • BeginInvoke 异步运行,因此非常安全 - 不会冻结您的应用程序。
  • 但我还需要在导航后等待完整的加载和渲染页面。如果我将使用 BeginInvoke() 它会冻结我的应用程序。你能用我的 CWebBrowser 对象运行你的解决方案吗?应用程序在 WaitOne() 方法处开始冻结 :(.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-01
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-26
相关资源
最近更新 更多