【问题标题】:WebBrowser control - wait until document completed extension methodWebBrowser 控件 - 等待文档完成扩展方法
【发布时间】:2016-03-16 21:19:17
【问题描述】:

我正在为WebBrowser 控件创建一个扩展方法,其目的是等待页面完成完全加载然后再返回。

完全加载,我的意思是在最后一个DocumentCompleted 事件之后必须经过一秒钟。这是为了考虑在页面加载期间触发多个 DocumentCompleted 事件的页面(这目前正在影响我的应用程序)。

我已经编写了当前的方法,它似乎可以工作,但我觉得可以改进:它在可能不需要时返回一个任务,并且它还存在如果应用程序关闭则会引发异常的问题在等待页面加载时。

    public static Task<bool> WaitLoad(this WebBrowser webBrowser, int wait)
    {
        var timerInternalWait = new Timer {Interval = 1000, Tag = "Internal"};
        var timerMaxWait = new Timer {Interval = wait};
        var tcs = new TaskCompletionSource<bool>();

        WebBrowserNavigatingEventHandler navigatingHandler = (sender, args) => timerInternalWait.Stop();
        webBrowser.Navigating += navigatingHandler;

        WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (sender, args) => { timerInternalWait.Stop(); timerInternalWait.Start(); };
        webBrowser.DocumentCompleted += documentCompletedHandler;

        EventHandler timerHandler = null;
        timerHandler = (sender, args) =>
        {
            webBrowser.Navigating -= navigatingHandler;
            webBrowser.DocumentCompleted -= documentCompletedHandler;
            timerInternalWait.Tick -= timerHandler;
            timerMaxWait.Tick -= timerHandler;
            timerMaxWait.Stop();
            timerInternalWait.Stop();

            tcs.SetResult(((Timer) sender).Tag.ToString() == "Internal");
        };

        timerInternalWait.Tick += timerHandler;
        timerMaxWait.Tick += timerHandler;

        return tcs.Task;
    }

有什么可以改进的吗?

【问题讨论】:

  • 显然在开始导航和函数 WaitLoad 订阅事件 WebBrowser.DocumentCompleted 之间有一段时间。如果在订阅事件之前引发事件 DocumentCompleted 会发生什么?
  • here检查NavigateAsync
  • @HaraldDutch 在我问了这个问题之后,我自己意识到了这一点。虽然像 Noseratio 提出的解决方案有助于显式导航,但 Click() 并等待页面重新加载呢?因此,我将改为为 WebBrowser 控件创建一个包装器,这样事件订阅的持续时间与对象实例的持续时间一样长。
  • @Noseratio,如上面的评论中所述,您的答案仅适用于导航异步,而不适用于例如单击导致新页面加载的按钮的情况。另外,我不得不承认我不是投票的忠实拥护者——如果您进行投票,甚至使用DocumentCompleted 也毫无意义。除了页面可能完成(发出DocumentCompleted),然后发出重定向,当浏览器到达新服务器时,DOM 保持不变超过 500 毫秒,使用您的方法会错误地返回已完成导航。
  • @Noseratio,换句话说,我发现解决上述评论中问题的唯一方法是利用DocumentCompletedNavigating 事件。轮询 DOM 以获取更改永远不会可靠地工作。

标签: c# .net async-await webbrowser-control


【解决方案1】:

The design guidelines state that asynchronous methods should be suffixed.

那个计时器让你的代码更难阅读。试试这个:

public static Task WaitLoad(this WebBrowser webBrowser)
{
    var tcs = new TaskCompletionSource<object>();

    WebBrowserNavigatingEventHandler navigatingHandler = (sender, args) => timerInternalWait.Stop();
    webBrowser.Navigating += navigatingHandler;

    WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (sender, args) => tcs.SetResult(null);

    try
    {
        webBrowser.DocumentCompleted += documentCompletedHandler;

        await tcs.Task;
    }
    finally
    {
        webBrowser.DocumentCompleted -= documentCompletedHandler;

        await Task.Delay(1000);
    }

    return tcs.Task;
}

public static Task<bool> WaitLoad(this WebBrowser webBrowser, int timeout)
{
    var webBrowserTask = webBrowser.WaitLoad();

    return await Task.WhenAny(webBrowserTask, Task.Delay(timeout)) == webBrowserTask;
}

但您应该查看取消令牌而不是双计时器。

【讨论】:

  • 这与我所拥有的不太一样。它只等到 DocumentCompleted 事件被触发一次,然后在返回之前有 1 秒的静态等待。问题是 DocumentCompleted 可以触发多次。这就是 1 秒计时器的原因...
  • 我已经有一段时间没有使用 WebBrowser 控件了,但我记得。拿起我的代码,您可以将延迟放在需要的地方,而无需管理计时器。
  • 当然,但问题是……为什么还要订阅这些事件?可以暂时使用 IsBusy 属性,中间使用 Task.Delay(500);
  • 另一方面,特别要记住这是问题的唯一答案,如果你想让我潜在地标记答案是正确的,你需要编辑你的代码来做我实际上在问什么。您提到很容易选择您的代码并修改以执行我的要求,然后请将其作为答案提交,如果它符合预期,我将标记为正确。
猜你喜欢
  • 1970-01-01
  • 2013-10-31
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 2014-01-08
  • 2011-08-17
  • 2022-11-19
  • 2022-08-07
相关资源
最近更新 更多