2009-05-08 33 views
14

Antes de comenzar a escribir esta pregunta, yo estaba tratando de solucionar después deCómo usar el control WebBrowser Evento DocumentCompleted en C#?

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

El problema era, que si existe un marco flotante en una página web, un evento DocumentCompleted podría ser despedido más de una vez (después de cada documento se ha completado) Era muy probable que el programa hubiera intentado leer datos de DOM que no se completaron y, naturalmente, fallaron.

Pero de repente mientras se escribe esta pregunta '¿Qué pasaría si' monstruo me inspiró, y me fix'ed el problema, que yo estaba tratando de resolver. Como no logré hacer Google en esto, pensé que sería bueno publicarlo aquí.

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 
    } 

Por ahora, al menos, mi truco de 5 m parece estar funcionando bien.

Tal vez estoy realmente fracasar en la consulta de Google o MSDN, pero no puedo encontrar: "Cómo utilizar el evento DocumentCompleted control WebBrowser en C#?"

Observación: Después de aprender mucho sobre webcontrol, encontré que hace cosas FuNKY.

Incluso si detecta que el documento se ha completado, en la mayoría de los casos no será así para siempre. La actualización de la página se puede realizar de varias maneras: actualización de marcos, solicitud tipo AJAX o inserción en el lado del servidor (necesita tener algún control que admita la comunicación asíncrona y tenga interoperabilidad html o JavaScript). Además, algunos iframes nunca se cargarán, por lo que no es la mejor idea esperarlos para siempre.

Terminé usando:

if (e.Url != wb.Url) 
+0

¿Cuál es el estado de la propiedad IsBusy durante el evento DocumentCompleted? – AMissico

+0

IsBusy devolverá falso tan pronto como el primer cuadro esté listo. – Margus

+1

Solo una nota, su código no funcionará con varios marcos normales. –

Respuesta

14

Es posible que desee conocer el AJAX llama así.

Considere el uso de este:

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

+1, pero la parte else contendría el DOCUMENTO REAL COMPLETO donde como la condición if sería el IFRAME – pug

+0

@pug He editado la publicación para indicar eso. – AaronLS

0

que tenía que hacer algo similar. Lo que hago es usar ShDocVw directamente (agregando una referencia a todos los ensamblajes de interoperabilidad necesarios para mi proyecto). Entonces, no agrego el control WebBrowser a mi formulario, pero el control AXShDocVw.AxWebBrowser.

Para navegar y esperar que utilizo para siguiente método:

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

Solo una nota a esto, fallará es que la página está usando Ajax, ya que la página nunca será "Completa". –

3

todavía tengo que encontrar una solución a este problema de trabajo en línea. Espero que esto llegue a la cima y le ahorre a todos los meses de ajustes que invertí tratando de resolverlo, y los casos extremos asociados con él. He luchado sobre este tema a lo largo de los años ya que Microsoft ha cambiado la implementación/confiabilidad de isBusy y document.readystate. Con IE8, tuve que recurrir a la siguiente solución. Es similar a la pregunta/respuesta de Margus con algunas excepciones. Mi código manejará marcos anidados, solicitudes javascript/ajax y meta-redirects. He simplificado el código para mayor claridad, pero también uso una función de tiempo de espera (no incluida) para restablecer la página web después de 5 minutos domAccess igual a falso.

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

¿podría explicar las diferencias con las versiones anteriores de IE? – peterchen

+1

En mis primeros años de automatización webbrowser pude simplemente usar la función documentcomplete - esto fue alrededor de 2002/3 - es decir, 5.5/6. Si su objeto pdisp coincidía con la ventana superior del documento, entonces el documento estaba completamente listo y siempre disparaba. En estos días ~ cuando ~ dispara, puede estar seguro de que su documento está listo, pero ¿cómo sabe cuándo un evento no ocurre en un modelo de evento asíncrono sin un gran tiempo de espera ineficiente? IsBusy solía ser un buen indicador para cambiar el cursor del mouse, pero he visto que IsBusy se mantiene verdadero indefinidamente en la versión reciente de 7 y 8. –

+0

Y si recuerdo correctamente las solicitudes AJAX no desencadenaron estos eventos correctamente hasta IE6, y desde ie7 o ie8 ahora activan duplicados antes de los eventos de navegación. Y no se moleste en navegar o descargar eventos completos, ya que no lo ayudarán a determinar el estado de finalización de un ciclo de vida de navegación. –

2

A diferencia de Thorsten que no tuvieron que utilizar SHDOCVW, pero lo que hizo la diferencia para mí estaba añadiendo el bucle de control de ReadyState y el uso de la aplicación.DoEvents() mientras no está listo. Aquí está mi código:

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

Y solía solución de Yuki para comprobar los resultados de WebBrowser_DocumentCompleted, aunque con el último if/else intercambiados por el comentario de usuario:

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

funcionó como un encanto :)

-1

Acabo de pensar aquí una o dos líneas sobre una pequeña mejora que funciona en conjunto con el código de FeiBao. La idea es inyectar una variable de punto de referencia (javascript) en la página web y usarla para detectar cuál de los siguientes eventos de DocumentComplete es el verdadero negocio. Dudo que sea a prueba de balas, pero ha funcionado de manera más confiable en general que el enfoque que le falta. Cualquier comentario bienvenido. Aquí está el código repetitivo:

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