如何在C#中使用WebBrowser控件DocumentCompleted事件?

时间:2009-05-08 16:54:34

标签: c# automation c#-2.0 webbrowser-control

在开始写这个问题之前,我试图解决以下问题

// 1. navigate to page
// 2. wait until page is downloaded
// 3. read and write some data from/to iframe 
// 4. submit (post) form

问题是,如果网页上存在iframe,则DocumentCompleted事件将被触发多次(在每个文档完成之后)。程序很可能试图从DOM中读取未完成且自然失败的数据。

但是在写这个问题的时候突然 '如果'怪物 会激励我,我解决了问题,那就是我试图解决的问题。由于我没有谷歌这个,我认为将它发布在这里会很好。

    private int iframe_counter = 1; // needs to be 1, to pass DCF test
    public bool isLazyMan = default(bool);

    /// <summary>
    /// LOCK to stop inspecting DOM before DCF
    /// </summary>
    public void waitPolice() {
        while (isLazyMan) Application.DoEvents();
    }

    private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e) {
        if(!e.TargetFrameName.Equals(""))
            iframe_counter --;
        isLazyMan = true;
    }

    private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
        if (!((WebBrowser)sender).Document.Url.Equals(e.Url))
            iframe_counter++;
        if (((WebBrowser)sender).Document.Window.Frames.Count <= iframe_counter) {//DCF test
            DocumentCompletedFully((WebBrowser)sender,e);
            isLazyMan = false; 
        }
    }

    private void DocumentCompletedFully(WebBrowser sender, WebBrowserDocumentCompletedEventArgs e){
        //code here
    }

至少现在,我的500万黑客似乎工作正常。

也许我真的无法查询谷歌或MSDN,但我找不到: “如何在C#中使用webbrowser控件DocumentCompleted事件?”

备注:在了解了很多关于webcontrol的内容之后,我发现它确实是FuNKY的东西。

即使您检测到文档已完成,在大多数情况下它也不会永远保持这样。页面更新可以通过多种方式完成 - 帧刷新,类似请求的ajax或服务器端推送(需要一些支持异步通信的控件并具有html或JavaScript互操作)。还有一些iframe永远不会加载,所以永远等待它们并不是最好的选择。

我最终使用了:

if (e.Url != wb.Url)

5 个答案:

答案 0 :(得分:14)

您可能也想知道AJAX调用。

考虑使用:

private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    string url = e.Url.ToString();
    if (!(url.StartsWith("http://") || url.StartsWith("https://")))
    {
            // in AJAX
    }

    if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
    {
            // IFRAME 
    }
    else
    {
            // REAL DOCUMENT COMPLETE
    }
}

答案 1 :(得分:3)

我还没有在网上找到解决这个问题的方法。希望这将使它成为顶级并为每个人节省我花费在试图解决它的几个月,以及与之相关的边缘情况。多年来,我一直在争论这个问题,因为微软已经改变了isBusy和document.readystate的实现/可靠性。使用IE8,我不得不求助于以下解决方案。它类似于Margus的问题/答案,但有一些例外。我的代码将处理嵌套帧,javascript / ajax请求和元重定向。为了清晰起见,我已经简化了代码,但是如果5分钟domAccess仍然等于false,我也会使用超时功能(不包括在内)来重置网页。

private void m_WebBrowser_BeforeNavigate(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
{
    //Javascript Events Trigger a Before Navigate Twice, but the first event 
    //will contain javascript: in the URL so we can ignore it.
    if (!URL.ToString().ToUpper().StartsWith("JAVASCRIPT:"))
    {
        //indicate the dom is not available
        this.domAccess = false;
        this.activeRequests.Add(URL);
    }
}

private void m_WebBrowser_DocumentComplete(object pDisp, ref object URL) 
{

    this.activeRequests.RemoveAt(0);

    //if pDisp Matches the main activex instance then we are done.
    if (pDisp.Equals((SHDocVw.WebBrowser)m_WebBrowser.ActiveXInstance)) 
    {
        //Top Window has finished rendering 
        //Since it will always render last, clear the active requests.
        //This solves Meta Redirects causing out of sync request counts
        this.activeRequests.Clear();
    }
    else if (m_WebBrowser.Document != null)
    {
        //Some iframe completed dom render
    }

    //Record the final complete URL for reference
    if (this.activeRequests.Count == 0)
    {
        //Finished downloading page - dom access ready
        this.domAccess = true;
    }
}

答案 2 :(得分:2)

与Thorsten不同,我不必使用ShDocVw,但是对我来说有什么不同的是添加循环检查ReadyState并使用Application.DoEvents()而没有准备好。这是我的代码:

        this.webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
        foreach (var item in this.urlList) // This is a Dictionary<string, string>
        {
            this.webBrowser.Navigate(item.Value);
            while (this.webBrowser1.ReadyState != WebBrowserReadyState.Complete)
            {
                Application.DoEvents();
            }
        }

我使用Yuki的解决方案来检查WebBrowser_DocumentCompleted的结果,尽管每个用户的评论都是最后一次if / else交换:

     private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        string url = e.Url.ToString();
        var browser = (WebBrowser)sender;

        if (!(url.StartsWith("http://") || url.StartsWith("https://")))     
        {             
            // in AJAX     
        }
        if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)     
        {
            // IFRAME           
        }     
        else     
        {             
            // REAL DOCUMENT COMPLETE
            // Put my code here
        }
    }

像魅力一样工作:)

答案 3 :(得分:0)

我必须做类似的事情。我所做的是直接使用ShDocVw(在我的项目中添加对所有必要的互操作程序集的引用)。然后,我不将WebBrowser控件添加到我的表单,而是 AXShDocVw.AxWebBrowser 控件。

要导航并等待我使用以下方法:

private void GotoUrlAndWait(AxWebBrowser wb, string url)
{
    object dummy = null;
    wb.Navigate(url, ref dummy, ref dummy, ref dummy, ref dummy);

    // Wait for the control the be initialized and ready.
    while (wb.ReadyState != SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
        Application.DoEvents();
}

答案 4 :(得分:-1)

只想在这里放一两行关于与飞豹代码一起工作的小改进。我们的想法是在网页中注入一个里程碑(javascript)变量,并使用它来检测哪些后续DocumentComplete事件是真正的交易。我怀疑它是防弹的,但它总体上比缺乏它的方法更可靠。任何评论欢迎。这是样板代码:

 void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        string url = e.Url.ToString();
        var browser = (WebBrowser)sender;

        if (!(url.StartsWith("http://") || url.StartsWith("https://")))
        {
            // in AJAX     
        }
        if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
        {
            // IFRAME           
        }
        else if (browser.Document != null && (bool)browser.Document.InvokeScript("eval", new object[] { @"typeof window.YourLandMarkJavascriptVariableHere === 'undefined'" }))
        {
            ((IHTMLWindow2)browser.Document.Window.DomWindow).execScript("var window.YourLandMarkJavascriptVariableHere = true;");

            // REAL DOCUMENT COMPLETE
            // Put my code here
        }
    }