【问题标题】:Loading large html file in webbrowser from another thread从另一个线程在 webbrowser 中加载大型 html 文件
【发布时间】:2012-05-25 00:35:19
【问题描述】:

我正在设计一个小型应用程序,到目前为止,我很幸运能够保持我的线程简单高效,因此 UI 是响应式的。

现在我面临一个问题,我需要在选项卡式 Web 浏览器控件中加载大型本地 html 文件。这些页面的加载会冻结用户界面。我看到很多线程都在谈论生活在另一个线程中的网络浏览器,但所有这些似乎都将网络浏览器排除在主用户界面之外。

我似乎无法让它正常工作。我尝试将 html 文件作为内存流加载到另一个线程中,然后将其提供给 webbrowser,我尝试在另一个线程中创建 webbrowser,但由于经典的线程间异常而关闭,尝试 SupendLayout...

现在,我知道线程规则指出您不能将在另一个线程中创建的控件添加到来自其他线程的控件...我的问题很简单,有没有办法加载显示的网络浏览器中的大型 html 文件,而这样做时不会冻结 ui?

如果这不是解决我的问题所必需的,则无需成为与线程相关的答案,但这是我最后一次也是一种蹩脚的尝试:

    public void openHtml(string input, bool isHtml = true, string tabTitle = "")
    {
        if (!this.loading)
        {
            this.loading = true;

            ManualResetEvent resetEvent = new ManualResetEvent(false);
            Panel panel = new Panel();
            TabPage tab = new TabPage();
            WebBrowser browser = null;

            Thread t = new Thread(() =>
            {
                browser = new WebBrowser();
                browser.CreateControl();
                browser.SuspendLayout();

                resetEvent.Set();

                if (isHtml)
                    browser.DocumentText = input;
                else
                    browser.Navigate(input);

                Application.Run();
            });

            t.IsBackground = true;
            t.SetApartmentState(ApartmentState.STA);
            t.Start();

            resetEvent.WaitOne();

            panel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            panel.Controls.Add(browser);
            panel.Dock = DockStyle.Fill;

            browser.ObjectForScripting = new JsScriptInterface(this, browser);
            browser.Dock = DockStyle.Fill;

            tab.Controls.Add(panel);
            tab.Text = tabTitle;

            this.tabs.TabPages.Add(tab);
        }
    }

任何帮助将不胜感激。谢谢

【问题讨论】:

  • 你检查过 t.SetApartmentState(ApartmentState.MTA); ?
  • 是的,我收到一条错误消息,指出它应该是 STA,因为 webbrowser 是一个 ActiveX 控件。仍然没有快乐。
  • 只在主线程上渲染 UI。您可以在后台线程上创建 HTML 或 FlowDocument 或 ...,但一旦开始呈现,您就无法控制回调。我所做的是检查尺寸并显示一个消息框“你确定”。如果它是一个支持虚拟化的 UI 控件,那么一切都很好。不幸的是,浏览器控件不支持虚拟化。我要做的是,如果选项卡未处于活动状态,则不加载该选项卡。在 MVVM 下它的工作方式不同。我希望你能得到更好的答案。
  • @ChG 你可以使用方法调用器。
  • @Blam 谢谢你的回答,我很害怕这个。这有点令人沮丧,因为在我的一次尝试中,我在另一个线程中创建了 webbrowser,它确实非常顺利地加载和渲染页面而没有冻结 ui...当它被添加到它的父控件时发生异常加载结束(触发 javascript onload 事件时)。

标签: c# .net winforms browser webbrowser-control


【解决方案1】:

我过去也遇到过类似的问题,可以告诉您没有“真正的”解决方案。如您所知,问题在于 UI 代码必须使用相同的线程,而不好的线程代码位于您无法控制的控件中(即 WebBrowser)。除非有人使用不会挂起 UI(不太可能)的适当线程编写更好的 WebBrowser 控件,否则我们不会有一个很棒的解决方案。

也就是说,有 2 个选项可能对您有用 - 我只与他们一起玩了一点,还没有分配时间进入生产能力状态。所以也许认为这是一个部分解决方案:/

  1. 在您的控件中托管非 UI 线程控件(ab)。
  2. 在屏幕外渲染和交换

在选项 1 中,链接讨论了将外部应用程序或来自另一个 AppDomain 的 WinForms 代码托管到您的应用程序/AppDomain 中。虽然我无法为您提供执行此操作所需的(复杂的?)代码,但我确实看到您可以在不同的线程(甚至可能在不同的 AppDomain)上拥有一个 WebBrowser 控件,然后使用所描述的技术在这些链接中显示此控件,就好像它在您的控件上一样。 WebBrowser 实际上不会在您的表单上,而是表单的一个区域会有一个覆盖层,它会随之调整大小/移动。我已经用它成功地在 WinForms 应用程序中托管了 Notepad++,调整大小有点笨拙但非常有用。您可以先尝试在普通 IE (iexplore.exe) 中呈现 HTML 文件并将该应用程序托管在您的应用程序中,一旦您开始工作,请尝试使用更可控的 ActiveX 包装器控件 (WebBrowser)。

选项 2 对我来说非常适合使用 RichTextBox,但我无法让 WebBrowser 以同样的成功响应。我所做的是使用 RichTextBox 生成一个隐藏的表单控件,加载我的数据并让它呈现。在我的主要可见表单上,我展示了另一个 RichTextBox,上面写着“正在加载...”或类似的内容。当 bg RichTextBox 完成后,我会将 Rtf 字段的内容移动到显示的控件中,并在眨眼之间呈现。可悲的是,WebBrowser 并没有公开对其内部数据(即 DOM)的相同级别的控制,但您可能能够在研究中比我更深入地使用 mshtml(cd)。

祝你好运!

【讨论】:

  • 非常感谢您的回答。我会尝试第一个选项。
猜你喜欢
  • 2020-07-19
  • 1970-01-01
  • 1970-01-01
  • 2017-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多