【问题标题】:is there an Application.DoEvents() for WebBrowser?WebBrowser 有 Application.DoEvents() 吗?
【发布时间】:2014-11-16 17:07:29
【问题描述】:

我正在使用Timer 来确定使用 AJAX 加载的页面是否准备就绪,并且仅在准备就绪时才从函数返回(包括 ajax 内容的页面加载已加载)。我正在尝试类似下面的代码,但我发现Application.DoEvents() 只处理挂起的 Windows 消息循环项,因此它运行到无限循环,因为Applicadion.DoEvents() 不会引发任何WebBrowser 的事件(但只有 Windows 一个作为我提到过)所以ReadyState 不能更新也永远不会改变。

我的问题是:有什么方法可以强制 WebBrowser's 事件 Application.DoEvents() 吗?

static bool done = false;
int[] foo()
{
  int[] data;
 timer1.Interval = 1000;
            timer1.Tick += new EventHandler(delegate(object o, EventArgs ea)
                {
                    if (browser.ReadyState == WebBrowserReadyState.Complete)
                    {
                        timer1.Stop();
                        data = extract_data();
                        done = true;
                    }
                });
            timer1.Start();

            while(!done) /* return from function only if timer1 isn't running anymore */
            {
                Application.DoEvents();
                Thread.Sleep(1000);
            }

            return data;
}

我知道Application.DoEvents()“问题”,但我找不到任何其他方法来做到这一点。一种不同的解决方法也非常受欢迎。

【问题讨论】:

    标签: c# .net events timer browser


    【解决方案1】:

    如果您使用 .NET 4.5 或更高版本(如果您愿意使用 Microsoft.Bcl.Async 库,则为 4.0),这可以通过 TaskCompletionSourceawait 轻松完成

    async Task<int[]> foo()
    {
        //Create the completion source and the callback delegate.
        var tcs = new TaskCompletionSource<object>();
        WebBrowserDocumentCompletedEventHandler callback = (sender, args) => tcs.SetResult(null);
    
        //Subscribe to the Document completed event and run the callback.
        browser.DocumentCompleted += callback;
    
        try
        {
            //We may already be in the complete state so the event will never fire.
            //Therefor if we are in the completed state we can skip the await.
            if (browser.ReadyState != WebBrowserReadyState.Complete)
            {
                //Wait here for the completed event to fire.
                await tcs.Task;
            }
        }
        finally
        {
            //Unsubscribe the callback which is nolonger needed.
            browser.DocumentCompleted -= callback;
        }
    
        //Process the data from the completed document.
        var data = extract_data();
        return data;
    }
    

    此代码将执行的操作是订阅DocumentCompleted 事件,然后如果文档尚未完成加载,则可以选择等待文档完成,同时它正在等待将控制权返回给调用者(与您的 DoEvents 效果相同循环,但更好)一旦事件触发,它就会处理数据并返回结果。

    但是,如果可能的话,一个更好的解决方案是重写您的代码,根本不调用foo,只需订阅DocumentCompleted 事件并将数据推送到需要去的地方而不是拉取它。

    【讨论】:

    • 首先,谢谢!这是我已经研究了一段时间的事情。这真的很有效,我一直在寻找。但不知何故,与我从 Timer 的滴答事件中调用 extract_data() 不同,预期的 div(在 ajax 调用之后创建)为空。好像负载还没有完成。但正如我所提到的,它适用于 Timer 的滴答事件(就像我在问题中的代码一样)。知道为什么会有这种行为吗?跟线程有关系吗?
    • 我的意思是预期的 div 是 null:考虑 var p = browser.Document.GetElementById("foo");(在 extract_data() 中声明)在 Timer 的滴答事件 p 不为空(所以我可以提取值)但使用您的解决方案p 为空。我不知道为什么。
    • 忘记所有以前的 cmets。我只是将这种方法与stackoverflow.com/questions/20930414/… 结合起来,效果很好
    【解决方案2】:

    在 Visual Studio 中双击 WebBrowser 控件。这将为 DocumentCompleted 事件创建一个事件处理程序。您可以使用任何其他机制来创建 DocumentCompleted 事件处理程序,但该事件是重要的部分。示例见我的文章Introduction to Web Site Scraping

    请不要为此使用 Application.DoEvents()、ReadyState 或 Thread.Sleep。

    如果网页使用脚本来生成页面的某些部分,则该问题可能难以解决。如果发生这种情况,我会尽我所能避免使用 Thread.Sleep,但你可能不得不这样做。

    【讨论】:

    • 该页面不可用。使用 DocumentCompleted 是行不通的,因为当 HTML 加载完成时会触发此事件,但在 AJAX 完成时我也需要。这就是Timer 的原因。我知道Application.DoEvents() 的问题,但在这个场景中我没有找到更好的东西。
    • 您说“DocumentCompleted 不起作用”,但我希望您明白,如果其他答案有效,则 DocumentCompleted 有效。我希望你明白这一点。
    • 这并不是我真正想要的方式,因为我想在包括 AJAX 在内的所有内容都准备好时处理页面,而不仅仅是像 DocumentCompleted 这样的 HTML。我将答案与这种方法结合起来,效果很好stackoverflow.com/questions/20930414/…
    猜你喜欢
    • 2013-01-30
    • 2011-10-11
    • 1970-01-01
    • 2011-07-08
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多