c# - 防止 WebBrowser 在导航时导致 UI 卡住?

标签 c# .net winforms asynchronous webbrowser-control

我面临的问题是,在处理 WebBrowser 控件(无论它是否可见)时,它会导致 UI 在导航时卡住一小段时间 ,这在必须顺序打开多个 URL 时变得非常明显且不可靠。

我目前正在使用 Noseratio's NavigateAsync静默和异步地导航到多个 URL 的扩展方法:(随意跳过阅读代码并继续问题)

public static async Task<string> NavigateAsync(this WebBrowser webBrowser, string url, CancellationToken token)
{
    var tcs = new TaskCompletionSource<bool>();
    WebBrowserDocumentCompletedEventHandler handler = (s, arg) => tcs.TrySetResult(true);

    using (token.Register(() => { webBrowser.Stop(); tcs.TrySetCanceled(); }, true))
    {
        webBrowser.DocumentCompleted += handler;
        try
        {
            webBrowser.Navigate(url);
            await tcs.Task; // wait for DocumentCompleted
        }
        finally
        {
            webBrowser.DocumentCompleted -= handler;
        }
    }

    var documentElement = webBrowser.Document.GetElementsByTagName("html")[0];
    var html = documentElement.OuterHtml;
    while (true)
    {
        await Task.Delay(POLL_DELAY, token);
        if (webBrowser.IsBusy)
            continue;

        var htmlNow = documentElement.OuterHtml;
        if (html == htmlNow) break; 

        html = htmlNow;
    }

    token.ThrowIfCancellationRequested();
    return html;
}

但即使是像下面这样最简单的代码:

WebBrowser wb = new WebBrowser() { ScriptErrorsSuppressed = true };
wb.Navigate("https://www.google.com/");

..还是一样的效果。

这是一个快速 demo video用尽可能简单的代码显示问题。

我也试过让 WebBrowser 在不同的 STA 线程上运行,但仍然没有成功。

那么,在处理 WebBrowser 时是否有办法避免卡住?


在您建议用 HttpClientWebClient 替换为 HTMLAgilityPack 之前,请注意我正在使用 WebBrowser 以获取显示的文本,格式尽可能接近它在浏览器中的显示方式(即尽可能接近手动选择和复制文本)。我尝试(或在网上找到)的每个解决方案不使用浏览器都未能实现这一点,即使是the one that produced the closest result不够好。

最佳答案

我可以确认你什么时候加载WebBrowser控件,UI 会卡住片刻,如果您使用 WebBrowser 的多个实例控制加载多个 url,滞后的 UI 很烦人,你不能与主窗口交互。

要重现该问题,您可以使用以下代码:

string google = "http://www.google.com";
var urls = Enumerable.Range(1, 100).Select(x => google).ToList();
foreach (var url in urls)
{
    var w = new WebBrowser() { ScriptErrorsSuppressed = true };
    w.DocumentCompleted += (obj, args) =>
        {
            var txt = ((WebBrowser)obj).DocumentText;
            this.textBox1.Text = DateTime.Now.ToString() + Environment.NewLine
                + txt.Substring(1, 200) + "...";
        };
    w.Navigate(url);
}

要解决这个问题,您可以创建一个方法来加载 WebBrowser在另一个线程中控制并返回 Task<string>当浏览器文档完成时完成。我创建了一个 BrowserBasedWebScraperthis post你可以用它来获取 WebBrowser 的内容在不滞后 UI 的情况下控制场景:

string google = "http://www.google.com";
var urls = Enumerable.Range(1, 100).Select(x => google).ToList();
foreach (var url in urls)
{
    var txt = await BrowserBasedWebScraper.LoadUrl(url);
    this.textBox1.Text = DateTime.Now.ToString() + Environment.NewLine
        + txt.Substring(1, 200) + "...";
}

您也可以download来自 repository 的工作示例.

关于c# - 防止 WebBrowser 在导航时导致 UI 卡住?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49096870/

相关文章:

vb.net - 如何创建显示绑定(bind)到对象列表的百分比的条形图?

c# - 模板生成器中 sitecore 'source' 字段的查询符号

c# - 从 C# 中删除数据库

c# - .Net 的系统区域设置/文化设置在哪里

c# - Azure Web 应用程序 - "HttpClient Unable to connect to the remote server"

c# - 如何从我创建的消息框中返回是或否?

c# - URI 比较器忽略查询字符串顺序

c# - 在 C# 中读取 SQL Server 数据库中的 "real"数据类型

c# - ClickOnce发布后找到 'Application Files'目录

c# - 如何将自定义 IDesigner 移动到同一解决方案中的单独程序集?