2009-03-23 14 views
11

Ich verwende .NET WebBrowser-Steuerelement. Woher weiß ich, wann eine Webseite vollständig geladen ist?HTML - Woher weiß ich, wenn alle Bilder geladen sind?

Ich möchte wissen, wenn der Browser keine Daten mehr abruft. (Der Moment, in dem der IE in seiner Statusleiste "Fertig" schreibt).

Hinweise:

  • Die Document/NavigateComplete Ereignisse mehrmals für eine Website mit mehreren Frames auftreten können.
  • Der Browser bereit Status löst das Problem auch nicht.
  • Ich habe versucht, die Anzahl der Frames in der Frame-Sammlung zu überprüfen und dann zählen, wie oft ich DocumentComplete-Ereignis erhalten, aber das funktioniert auch nicht.
  • this.WebBrowser.IsBusy funktioniert auch nicht. Es ist immer "falsch", wenn es im Document Complete-Handler geprüft wird.

Antwort

1

Hier ist, was für mich endlich geklappt:

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

Auf jedem Dokument komplette Veranstaltung, die ich über die ganze HTML-Element laufen und prüfen Sie alle Rahmen zur Verfügung (ich weiß, es optimiert werden kann). Für jeden Rahmen überprüfe ich seinen Bereitzustand. Es ist ziemlich zuverlässig, aber genau wie Jeffaffone sagte, ich habe bereits Seiten gesehen, die einige interne Aktualisierungen ausgelöst haben. Aber der obige Code erfüllt meine Bedürfnisse.

Edit: jeder Rahmen kann Frames enthalten, so dass ich denke, dass dieser Code aktualisiert werden sollte, um den Zustand jedes Rahmens rekursiv zu überprüfen.

0

Haben Sie versucht WebBrowser.IsBusy Eigentum?

+1

ja. Der Webbrowser behauptet, nicht jedes Mal beschäftigt zu sein, wenn der Document Complete-Handler aufgerufen wird ... –

0

Wie wäre es mit Javascript in jedem Rahmen, um eine Flagge zu setzen, wenn der Rahmen abgeschlossen ist, und dann C# auf die Fahnen schauen?

+0

Ich möchte nicht den DOM-Baum jeder Site manipulieren, zu der der Browser navigiert. Aber angenommen, ich verwende deine Lösung, wie mache ich das in Javascript? –

+0

Ich sehe nicht den Vorteil, dies in JS vs C# zu tun. –

0

Ich habe eine Alternative nicht für Sie, aber ich frage mich, ob die IsBusy Eigenschaft tru e während des Document kompletter Handler wird, da der Handler noch läuft und damit die WebBrowser Kontrolle ist technisch noch ‚beschäftigt‘.

Die einfachste Lösung wäre eine Schleife, die alle 100 ms oder so ausgeführt wird, bis das Flag IsBusy zurückgesetzt wird (mit einer maximalen Ausführungszeit im Falle von Fehlern). Das setzt natürlich voraus, dass IsBusy zu keiner Zeit während des Ladens der Seite auf false gesetzt wird.

Wenn der Document Complete-Handler in einem anderen Thread ausgeführt wird, können Sie eine Sperre verwenden, um den Hauptthread in den Ruhezustand zu versetzen und aus dem Thread "Document Complete" zu aktivieren. Dann überprüfen Sie die IsBusy Flag, wieder sperren den Haupt-Thread ist es immer noch true.

+0

Aber die IsBusy wird zu früh auf falsch gesetzt. Wenn Sie zum Beispiel auf einer Webseite sechs Frames haben, ist beim Laden des ersten Frames der IsBusy-Wert für das DocumentComplete-Ereignis falsch. –

+0

Jeder Rahmen erhält seinen eigenen Webbrowser (IWebBrowser2-Implementierung). Wahrscheinlich gilt das IsBusy-Attribut nur für den bestimmten Frame. Und wenn es fertig ist, ist es nicht mehr beschäftigt. –

0

Ich bin nicht sicher, dass es funktioniert, aber versuchen, einen JavaScript „onload“ -Ereignis auf Ihrem Frameset so hinzuzufügen:

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

Ich möchte wissen, ob alle Frames für eine Website geladen sind, so dass ich nicht weiß, welche Frames es enthält. –

+0

Sie sollten das auf dem Frameset (Eltern aller Frames) tun, nicht auf jedem Frame. Es ist ziemlich einfach, es von jeder Website wie folgt zu bekommen: document.getElementsByTagName ('frameset') [0] – paulgreg

0

Können Sie jQuery verwenden? Dann könnten Sie einfach Frame-Ready-Ereignisse auf den Zielframes binden. Siehe this, um eine Wegbeschreibung zu erhalten. Diese blog post hat auch eine Diskussion darüber. Schließlich gibt es eine plug-in, die Sie verwenden könnten.

Die Idee ist, dass Sie die Anzahl der Bilder in der Web-Seite mit count:

$("iframe").size() 

und dann zählen Sie, wie oft die iframe bereit Ereignis ausgelöst wurde.

0

Sie erhalten ein Ereignis BeforeNavigate und DocumentComplete für die äußere Webseite sowie für jeden Frame. Sie wissen, dass Sie fertig sind, wenn Sie das DocumentComplete-Ereignis für die äußere Webseite erhalten. Sie sollten das verwaltete Äquivalent von IWebBrowser2::TopLevelContainer() verwenden können, um dies zu bestimmen.

Vorsicht, aber die Website selbst kann mehr Rahmennavigationen auslösen, wann immer sie will, so dass Sie nie wissen, ob eine Seite wirklich für immer fertig ist. Das Beste, was Sie tun können, ist, die Anzahl der angezeigten BeforeNavigates zu zählen und die Anzahl zu verringern, wenn Sie eine DocumentComplete erhalten.

Edit: Hier ist die verwaltete Dokumentation: TopLevelContainer.

+1

Ich habe versucht zu zählen, bevor die navigiert und das Dokument im WebBrowser-Steuerelement abgeschlossen. Es ist nicht synchronisiert ... :(. Es gibt mehr vor der Navigation als das Dokument abgeschlossen. [Vielleicht hat es mit Caching oder doppelte Bilder, die abgerufen werden. Ich weiß es nicht]. –

+0

In Bezug auf das Dokument abgeschlossen Ereignis: in C# WebBrowser Sie erhalten nicht das Dokument Objekt, das gerade Laden beendet. Nur die URL. So können Sie nicht zu seinem Browser-Container. –

2

Mein Ansatz etwas zu tun, wenn Seite vollständig so etwas wie dieses (inklusive Rahmen) ist geladen:

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

Von meinen anderen Ansätze, die ich gelernt, einige „nicht“ -s:

  • Versuchen Sie nicht, den Löffel zu biegen ... ;-)
  • Versuchen Sie nicht, aufwendiges Konstrukt mit DocumentComplete, Frames, HtmlWindow.Load Ereignisse zu erstellen. Ihre Lösung wird fragil sein, wenn Sie überhaupt arbeiten.
  • Verwenden Sie nicht System.Timers.Timer anstelle von , seltsame Fehler werden in seltsamen Orten auftreten, wenn Sie tun, aufgrund von Timer auf anderen Thread, dass der Rest Ihrer App läuft.
  • Verwenden Sie nicht nur Timer ohne DocumentComplete, da es möglicherweise ausgelöst wird, bevor Ihre Seite geladen wird und Ihren Code vorzeitig ausführen wird.
2

Hier ist, wie ich das Problem in meiner Anwendung gelöst:

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

Wenn Sie zB einen Klick in eine Navigationsleiste und bewirkt, dass eine neue Website neu geladen wird in einem frame/iframe wirst du mit dieser lösung nicht zufrieden sein –

0

benutze ich nur die webBrowser.StatusText Methode. Wenn "Fertig" steht, ist alles geladen! Oder fehlt mir etwas?

+0

Negativ wenn es iframe gibt –

2

Hier ist meine getestete Version. Machen Sie einfach Ihre DocumentCompleted Event Handler und legen Sie den Code, der nur genannt werden soll einmal in die Methode OnWebpageReallyLoaded(). Effektiv bestimmt dieser Ansatz, wann die Seite für 200ms stabil war und macht dann ihre Sache.

// 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 */ 
} 
+0

Vielen dank, es funktioniert perfekt für mich. – selegnasol

0

Überprüfung auf IE.readyState = READYSTATE_COMPLETE sollte funktionieren, aber wenn das erweist sich nicht für Sie zuverlässig und Sie buchstäblich wollen „um den Moment, in dem IE in seiner Statusleiste auf‚Fertig‘, schreibt“ kennen, dann können Sie tun eine Schleife bis IE.StatusText "Done" enthält.