【问题标题】:HTML - How do I know when all frames are loaded?HTML - 我如何知道所有帧何时加载?
【发布时间】:2010-10-14 22:28:56
【问题描述】:

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

我想知道浏览器何时不再获取任何数据。 (当 IE 在其状态栏中写入“完成”的那一刻......)。

注意事项:

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

【问题讨论】:

    标签: c# html browser mshtml


    【解决方案1】:

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

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

    【讨论】:

    • 如果你这样做,例如单击导航栏会导致在框架/iframe 中重新加载新网站,您不会对此解决方案感到满意。
    【解决方案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,因为它可能会在您的页面开始加载之前触发,并且会提前执行您的代码。

    【讨论】:

      【解决方案3】:

      这是我测试过的版本。只需将其设为您的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 */
      }
      

      【讨论】:

        【解决方案4】:

        如何在每一帧中使用javascript在帧完成时设置一个标志,然后让C#查看标志?

        【讨论】:

        • 我不想操纵浏览器导航到的每个站点的 DOM 树。但是假设我确实使用了您的解决方案,我该如何在 javascript 中做到这一点?
        • 我没有看到在 JS 与 C# 中这样做的优势。
        【解决方案5】:

        我不确定它是否会起作用,但请尝试在您的框架集上添加一个 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); 
        

        【讨论】:

        • 我想知道是否为任何网站加载了所有框架,所以我不知道它包含哪些框架。
        • 您应该在框架集(所有框架的父级)上执行此操作,而不是在每个框架上执行此操作。从任何这样的网站上获取它都非常容易:document.getElementsByTagName('frameset')[0]
        【解决方案6】:

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

        这个想法是您使用以下方法计算网页中的帧数:

        $("iframe").size()
        

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

        【讨论】:

          【解决方案7】:

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

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

          编辑:这是托管文档:TopLevelContainer

          【讨论】:

          • 我尝试在 WebBrowser 控件中计算之前的导航和文档完成。它没有同步... :(。导航之前的内容比文档完成的要多。[可能与缓存或提取的重复帧有关。我不知道]。
          • 关于文档完成事件:在 C# WebBrowser 中,您不会获得刚刚完成加载的文档对象。只是网址。所以你无法访问它的浏览器容器。
          【解决方案8】:

          这就是最终对我有用的方法:

                 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 所说,我已经看到一些网站触发了一些内部刷新。 但是上面的代码满足了我的需求。

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

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                【解决方案11】:

                你试过WebBrowser.IsBusy属性吗?

                【讨论】:

                • 是的。每次调用文档完成处理程序时,Web 浏览器都声称不忙...
                【解决方案12】:

                我没有其他选择,但我想知道在 Document Complete 处理程序期间 IsBusy 属性为 true 是否是因为处理程序仍在运行,因此 WebBrowser 控件在技术上仍然“忙碌” '。

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

                如果 Document Complete 处理程序在另一个线程上执行,您可以使用锁将主线程发送到睡眠状态并从 Document Complete 线程中唤醒它。然后检查IsBusy标志,重新锁定主线程仍然是true

                【讨论】:

                • 但是 IsBusy 设置为 false 太早了。例如,如果您的网页中有六个框架,当第一个框架完成加载时,在 DocumentComplete 事件中 IsBusy 为 false。
                • 每个框架都有自己的网络浏览器(IWebBrowser2 实现)。可能 IsBusy 属性仅适用于特定框架。完成后,它就不再忙了。
                猜你喜欢
                • 1970-01-01
                • 2023-01-05
                • 2016-09-19
                • 1970-01-01
                • 1970-01-01
                • 2011-05-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多