HTML - 如何知道何时加载所有帧?

时间:2009-03-23 09:44:48

标签: c# html browser mshtml

我正在使用.NET WebBrowser控件。 如何知道网页何时完全加载?

我想知道浏览器何时无法获取更多数据。 (当IE在状态栏中写'完成'时......)。

注意:

  • 对于包含多个框架的网站,可能会多次发生DocumentComplete / NavigateComplete事件。
  • 浏览器就绪状态也无法解决问题。
  • 我已经尝试检查帧集合中的帧数,然后计算我获得DocumentComplete事件的次数,但这也不起作用。
  • this.WebBrowser.IsBusy也不起作用。在文档完成处理程序中检查它时总是“假”。

12 个答案:

答案 0 :(得分:2)

我在页面完全加载(包括框架)时执行的方法是这样的:

using System.Windows.Forms;
    protected delegate void Procedure();
    private void executeAfterLoadingComplete(Procedure doNext) {
        WebBrowserDocumentCompletedEventHandler handler = null;
        handler = delegate(object o, WebBrowserDocumentCompletedEventArgs e)
        {
            ie.DocumentCompleted -= handler;
            Timer timer = new Timer();
            EventHandler checker = delegate(object o1, EventArgs e1)
            {
                if (WebBrowserReadyState.Complete == ie.ReadyState)
                {
                    timer.Dispose();
                    doNext();
                }
            };
            timer.Tick += checker;
            timer.Interval = 200;
            timer.Start();
        };
        ie.DocumentCompleted += handler;
    }

从我的其他方法中我学到了一些“不要”-s:

  • 不要试图弯曲勺子;; - )
  • 不要尝试使用DocumentComplete,Frames,HtmlWindow.Load事件构建精细的构造。如果可以的话,你的解决方案将是脆弱的。
  • 不要使用System.Timers.Timer代替Windows.Forms.Timer,如果你这样做,奇怪的错误会在陌生的地方发生,因为你的应用程序的其他部分在不同的线程上运行计时器。
  • 不要只使用没有DocumentComplete的Timer,因为它可能会在您的页面开始加载之前触发,并且会过早地执行您的代码。

答案 1 :(得分:2)

以下是我在应用程序中解决问题的方法:

private void wbPost_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    if (e.Url != wbPost.Url)
        return;
    /* Document now loaded */
}

答案 2 :(得分:2)

这是我测试过的版本。只需将其设为DocumentCompleted Event Handler,然后将您只需要一次的代码放入方法OnWebpageReallyLoaded()即可。实际上,这种方法可以确定页面何时稳定200毫秒然后完成它。

// event handler for when a document (or frame) has completed its download
Timer m_pageHasntChangedTimer = null;
private void webBrowser_DocumentCompleted( object sender, WebBrowserDocumentCompletedEventArgs e ) {
    // dynamic pages will often be loaded in parts e.g. multiple frames
    // need to check the page has remained static for a while before safely saying it is 'loaded'
    // use a timer to do this

    // destroy the old timer if it exists
    if ( m_pageHasntChangedTimer != null ) {
        m_pageHasntChangedTimer.Dispose();
    }

    // create a new timer which calls the 'OnWebpageReallyLoaded' method after 200ms
    // if additional frame or content is downloads in the meantime, this timer will be destroyed
    // and the process repeated
    m_pageHasntChangedTimer = new Timer();
    EventHandler checker = delegate( object o1, EventArgs e1 ) {
        // only if the page has been stable for 200ms already
        // check the official browser state flag, (euphemistically called) 'Ready'
        // and call our 'OnWebpageReallyLoaded' method
        if ( WebBrowserReadyState.Complete == webBrowser.ReadyState ) {
            m_pageHasntChangedTimer.Dispose();
            OnWebpageReallyLoaded();
        }
    };
    m_pageHasntChangedTimer.Tick += checker;
    m_pageHasntChangedTimer.Interval = 200;
    m_pageHasntChangedTimer.Start();
}

OnWebpageReallyLoaded() {
    /* place your harvester code here */
}

答案 3 :(得分:1)

这是最终对我有用的东西:

       public bool WebPageLoaded
    {
        get
        {
            if (this.WebBrowser.ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete)
                return false;

            if (this.HtmlDomDocument == null)
                return false;

            // iterate over all the Html elements. Find all frame elements and check their ready state
            foreach (IHTMLDOMNode node in this.HtmlDomDocument.all)
            {
                IHTMLFrameBase2 frame = node as IHTMLFrameBase2;
                if (frame != null)
                {
                    if (!frame.readyState.Equals("complete", StringComparison.OrdinalIgnoreCase))
                        return false;

                }
            }

            Debug.Print(this.Name + " - I think it's loaded");
            return true;
        }
    }

在每个文档完成事件中,我遍历所有html元素并检查所有可用的帧(我知道它可以被优化)。对于每个帧,我检查其就绪状态。 它非常可靠,但就像jeffamaphone说我已经看到了引发一些内部刷新的网站。 但上面的代码满足了我的需求。

编辑:每个帧都可以包含其中的帧,所以我认为应该更新此代码以递归检查每个帧的状态。

答案 4 :(得分:0)

您是否尝试过WebBrowser.IsBusy属性?

答案 5 :(得分:0)

如果在框架完成时在每个框架中使用javascript来设置标志,然后让C#查看标志?

答案 6 :(得分:0)

我没有替代方案,但我想知道在文档完成处理程序中IsBusy属性是tru e是因为处理程序仍在运行,因此{{1}控制在技术上仍然“忙碌”。

最简单的解决方案是让一个每100 ms左右执行一次的循环,直到重置WebBrowser标志(如果出现错误,则执行时间最长)。当然,这假设在页面加载期间的任何时候都不会将IsBusy设置为IsBusy

如果文档完成处理程序在另一个线程上执行,则可以使用锁定将主线程发送到休眠状态并从文档完成线程中将其唤醒。然后检查false标志,重新锁定主线程仍为IsBusy

答案 7 :(得分:0)

我不确定它是否可行,但尝试在您的框架集上添加一个JavaScript“onload”事件:

function everythingIsLoaded() { alert("everything is loaded"); }
var frameset = document.getElementById("idOfYourFrameset");
if (frameset.addEventListener)
    frameset.addEventListener('load',everythingIsLoaded,false); 
else
    frameset.attachEvent('onload',everythingIsLoaded); 

答案 8 :(得分:0)

你能用jQuery吗?然后,您可以轻松地在目标帧上绑定帧就绪事件。有关说明,请参阅this答案。这个blog post也有讨论。最后,您可以使用plug-in

我们的想法是使用以下方法计算网页中的帧数:

$("iframe").size()

然后计算iframe就绪事件被触发的次数。

答案 9 :(得分:0)

您将获得外部网页以及每个框架的BeforeNavigate和DocumentComplete事件。当您获得外部网页的DocumentComplete事件时,您就知道已经完成了。您应该能够使用IWebBrowser2::TopLevelContainer()的托管等值来确定这一点。

但是,请注意,网站本身可以随时触发更多的帧导航,因此您永远不会知道页面是否真正永久完成。您可以做的最好的事情是保留您看到的所有BeforeNavigates的计数,并在获得DocumentComplete时减少计数。

修改:这是托管文档:TopLevelContainer

答案 10 :(得分:0)

我只使用webBrowser.StatusText方法。当它说“完成”时,一切都已加载! 或者我错过了什么?

答案 11 :(得分:0)

检查IE.readyState = READYSTATE_COMPLETE应该可以工作,但是如果这对你来说不可靠并且你真的想知道“IE在其状态栏中写'完成'的那一刻”,那么你可以做一个循环直到IE .StatusText包含“完成”。