简体   繁体   English

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

[英]How to use WebBrowser control DocumentCompleted event in C#?

Before starting writing this question, i was trying to solve following 在开始写这个问题之前,我试图解决以下问题

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

The problem was, that if a iframe exists on a web page, DocumentCompleted event would get fired more then once (after each document has been completed). 问题是,如果网页上存在iframe,则DocumentCompleted事件将被触发多次(在每个文档完成之后)。 It was highly likely that program would have tried to read data from DOM that was not completed and naturally - fail. 程序很可能试图从DOM中读取未完成且自然失败的数据。

But suddenly while writing this question 'What if' monster inspired me, and i fix'ed the problem, that i was trying to solve. 但突然写下这个问题'如果'怪物启发了我,我解决了问题,我试图解决。 As i failed Google'ing this, i thought it would be nice to post it here. 由于我没有谷歌这个,我觉得在这里发布它会很好。

    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
    }

For now at least, my 5m hack seems to be working fine. 至少现在,我的500万黑客似乎工作正常。

Maybe i am really failing at querying google or MSDN, but i can not find: "How to use webbrowser control DocumentCompleted event in C# ?" 也许我真的没有查询谷歌或MSDN,但我找不到:“如何在C#中使用webbrowser控件DocumentCompleted事件?”

Remark: After learning a lot about webcontrol, I found that it does FuNKY stuff. 备注:在学习了很多关于webcontrol之后,我发现它确实是FuNKY的东西。

Even if you detect that the document has completed, in most cases it wont stay like that forever. 即使您检测到文档已完成,在大多数情况下它也不会永远保持这样。 Page update can be done in several ways - frame refresh, ajax like request or server side push (you need to have some control that supports asynchronous communication and has html or JavaScript interop). 页面更新可以通过多种方式完成 - 帧刷新,类似请求的ajax或服务器端推送(需要一些支持异步通信的控件并具有html或JavaScript互操作)。 Also some iframes will never load, so it's not best idea to wait for them forever. 还有一些iframe永远不会加载,所以永远等待它们并不是最好的选择。

I ended up using: 我最终使用:

if (e.Url != wb.Url)

You might want to know the AJAX calls as well. 您可能也想知道AJAX调用。

Consider using this: 考虑使用这个:

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

I have yet to find a working solution to this problem online. 我还没有在网上找到解决这个问题的方法。 Hopefully this will make it to the top and save everyone the months of tweaking I spent trying to solve it, and the edge cases associated with it. 希望这将使它成为顶级并为每个人节省我花费在试图解决它的几个月,以及与之相关的边缘情况。 I have fought over this issue over the years as Microsoft has changed the implementation/reliability of isBusy and document.readystate. 多年来,我一直在争论这个问题,因为微软已经改变了isBusy和document.readystate的实现/可靠性。 With IE8, I had to resort to the following solution. 使用IE8,我不得不求助于以下解决方案。 It's similar to the question/answer from Margus with a few exceptions. 它类似于Margus的问题/答案,但有一些例外。 My code will handle nested frames, javascript/ajax requests and meta-redirects. 我的代码将处理嵌套帧,javascript / ajax请求和元重定向。 I have simplified the code for clarity sake, but I also use a timeout function (not included) to reset the webpage after if 5 minutes domAccess still equals false. 为了清晰起见,我已经简化了代码,但是如果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;
    }
}

Unlike Thorsten I didn't have to use ShDocVw, but what did make the difference for me was adding the loop checking ReadyState and using Application.DoEvents() while not ready. 与Thorsten不同,我不必使用ShDocVw,但是对我来说有什么不同的是添加循环检查ReadyState并使用Application.DoEvents()而没有准备好。 Here is my code: 这是我的代码:

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

And I used Yuki's solution for checking the results of WebBrowser_DocumentCompleted, though with the last if/else swapped per user's comment: 我使用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
        }
    }

Worked like a charm :) 像魅力一样工作:)

I had to do something similar. 我不得不做类似的事情。 What I do is use ShDocVw directly (adding a reference to all the necessary interop assemblies to my project). 我所做的是直接使用ShDocVw(在我的项目中添加对所有必要的互操作程序集的引用)。 Then, I do not add the WebBrowser control to my form, but the AXShDocVw.AxWebBrowser control. 然后,我不将WebBrowser控件添加到我的窗体,而是添加AXShDocVw.AxWebBrowser控件。

To navigate and wait I use to following method: 要导航并等待我使用以下方法:

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

Just thought to drop a line or two here about a small improvement which works in conjunction with the code of FeiBao. 只是想在这里放一两行关于一个小的改进,它与飞豹的代码一起工作。 The idea is to inject a landmark (javascript) variable in the webpage and use that to detect which of the subsequent DocumentComplete events is the real deal. 我们的想法是在网页中注入一个里程碑(javascript)变量,并使用它来检测哪些后续DocumentComplete事件是真正的交易。 I doubt it's bulletproof but it has worked more reliably in general than the approach that lacks it. 我怀疑它是防弹的,但它总体上比缺乏它的方法更可靠。 Any comments welcome. 任何评论欢迎。 Here is the boilerplate code: 这是样板代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM