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

如何在C#中使用WebBrowser控件DocumentCompleted事件?,c#,automation,c#-2.0,webbrowser-control,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中读取未完成且自然失败的数据 但是在写这个问题的时候,突然间,

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

// 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
    }

我不得不做类似的事情。我所做的是直接使用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();
}

我还没有在网上找到解决这个问题的有效方法。希望这将使它的顶部,并节省每个人几个月的调整,我花了试图解决它,以及与之相关的边缘案件。多年来,随着微软改变了iBusy和document.readystate的实现/可靠性,我一直在为这个问题争论不休。对于IE8,我不得不求助于以下解决方案。这与Margus的问题/答案相似,只有少数例外。我的代码将处理嵌套帧、javascript/ajax请求和元重定向。为了清晰起见,我简化了代码,但如果5分钟后domcaccess仍然等于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;
    }
}

您可能还想知道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
    }
}

与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();
            }
        }

工作起来很有魅力:)

我想在这里写一两行关于飞豹代码的小改进。其想法是在网页中注入一个landmark(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
        }
    }

DocumentCompleted事件期间IsBusy属性的状态是什么?第一个帧准备就绪后,IsBusy将返回false。请注意,您的代码将无法处理多个正常帧。请注意,如果页面使用Ajax,它将失败,因为页面永远不会“完成”.你能详细说明一下与以前IE版本的区别吗?在我从事webbrowser自动化的早期,我能够简单地使用documentcomplete功能-大约是2002/3-ie5.5/6。如果您的pdisp对象与顶部文档窗口匹配,则文档已完全准备就绪,并且始终处于激发状态。现在~当~它真的启动时,您可以确信您的文档已经准备好了,但是您如何知道在异步事件模型中,如果没有大量无效超时,事件何时不会发生?IsBusy曾经是更改鼠标光标的一个很好的指示器,但我看到IsBusy在最新版本的7&8中无限期地保持为真。如果我没记错的话,AJAX请求直到IE6才正确触发这些事件,并且由于ie7或ie8,它们现在在导航事件之前触发重复事件。甚至不用担心导航完成或下载完成事件,因为它们不会帮助您确定导航生命周期的完成状态。+1,但else部分将包含真正的文档完成,其中if条件将是IFRAME
     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
        }
    }
 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
        }
    }