14

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

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

至少现在,我的 5m hack 似乎运行良好。

也许我在查询 google 或 MSDN 时真的失败了,但我找不到:“如何在 C# 中使用 webbrowser 控制 DocumentCompleted 事件?”

备注:在学习了很多关于webcontrol的知识后,我发现它做了Funky的东西。

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

我最终使用:

if (e.Url != wb.Url)
4

5 回答 5

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
    }
}
于 2010-06-16T05:39:10.297 回答
3

我还没有在网上找到解决这个问题的有效方法。希望这将使它达到顶峰,并为每个人节省我为解决它而花费的几个月的调整时间,以及与之相关的边缘情况。多年来,随着 Microsoft 改变了 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;
    }
}
于 2009-10-13T15:51:24.737 回答
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
        }
    }

像魅力一样工作:)

于 2011-02-01T21:21:24.427 回答
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();
}
于 2009-06-05T07:19:37.600 回答
-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
        }
    }
于 2011-09-12T18:04:47.633 回答