2009-05-08 4 views
14

Prima di iniziare a scrivere a questa domanda, stavo cercando di risolvere i seguentiCome utilizzare il controllo WebBrowser evento DocumentCompleted in C#?

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

Il problema era, che se un iframe esiste in una pagina web, evento DocumentCompleted otterrebbe sparato più di una volta (dopo ogni documento è stato completato). Era molto probabile che il programma avrebbe cercato di leggere i dati dal DOM che non era stato completato e, naturalmente, fallire.

Ma improvvisamente durante la scrittura di questa domanda 'E se' mostro mi ha ispirato, e ho fix'ed il problema, che stavo cercando di risolvere. Come ho fallito Google, ho pensato che sarebbe stato bello postarlo qui.

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 
    } 

Per ora almeno, il mio mod 5m sembra funzionare bene.

Forse non riesco davvero a interrogare google o MSDN, ma non riesco a trovare: "Come utilizzare il controllo del webbrowser evento DocumentCompleted in C#?"

Nota: Dopo aver appreso molto sul webcontrol, ho scoperto che fa cose FuNKY.

Anche se si rileva che il documento è stato completato, nella maggior parte dei casi non rimarrà tale per sempre. L'aggiornamento delle pagine può essere eseguito in diversi modi: aggiornamento dei frame, richiesta analogica o push lato server (è necessario disporre di un controllo che supporti la comunicazione asincrona e abbia l'interoperabilità HTML o JavaScript). Inoltre, alcuni iframe non verranno mai caricati, quindi non è consigliabile aspettarli per sempre.

ho finito per usare:

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

Qual è lo stato della proprietà IsBusy durante l'evento DocumentCompleted? – AMissico

+0

IsBusy restituirà false non appena il primo frame è pronto. – Margus

+1

Solo una nota, il tuo codice non funzionerà con più frame normali. –

risposta

14

Si consiglia di conoscere anche le chiamate AJAX.

Considerare l'utilizzo di questo:

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, ma la parte else conterrebbe il REAL DOCUMENT COMPLETE dove la condizione if sarebbe IFRAME – pug

+0

@pug Ho modificato il post per indicare che. – AaronLS

0

che dovevo fare qualcosa di simile. Quello che faccio è usare direttamente ShDocVw (aggiungendo un riferimento a tutti gli assembly di interoperabilità necessari al mio progetto). Quindi, non aggiungo il controllo WebBrowser al mio modulo, ma il controllo AXShDocVw.AxWebBrowser.

Per navigare e attendere che uso per seguente metodo:

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 questo, fallirà è la pagina sta usando Ajax, come la pagina non sarà mai "Completa". –

3

Devo ancora trovare una soluzione di lavoro a questo problema in linea. Spero che questo possa arrivare al vertice e salvare tutti i mesi di modifiche che ho speso cercando di risolverlo, e i casi limite associati ad esso. Ho combattuto su questo problema nel corso degli anni, in quanto Microsoft ha cambiato l'implementazione/l'affidabilità di isBusy e document.readystate. Con IE8, ho dovuto ricorrere alla seguente soluzione. È simile alla domanda/risposta di Margus con alcune eccezioni. Il mio codice gestirà frame annidati, richieste javascript/ajax e meta-reindirizzamenti. Ho semplificato il codice per motivi di chiarezza, ma uso anche una funzione di timeout (non inclusa) per ripristinare la pagina Web dopo 5 minuti se domAccess è ancora uguale a 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; 
    } 
} 
+0

potresti forse approfondire le differenze rispetto alle precedenti versioni di IE? – peterchen

+1

Nei miei primi anni di automazione del browser sono stato in grado di utilizzare semplicemente la funzione documentcomplete, ovvero circa il 2002/3, ovvero 5,5/6. Se il tuo oggetto pdisp corrisponde alla finestra del documento superiore, il documento era completamente pronto e veniva sempre attivato. In questi giorni ~ quando fa fuoco puoi essere certo che il tuo documento è pronto, ma come fai a sapere quando un evento non si verifica in un modello di evento asincrono senza un timeout inefficiente? IsBusy era un buon indicatore per cambiare il cursore del mouse, ma ho visto IsBusy rimanere vero per un tempo indefinito nella versione recente di 7 e 8. –

+0

E se richiamo correttamente le richieste AJAX non ha attivato correttamente questi eventi fino a IE6, e da allora ie7 o ie8 ora attivano il duplicato prima di navigare negli eventi. E non preoccupatevi nemmeno di navigare completi o di scaricare eventi completi in quanto non vi aiuteranno a determinare lo stato di completamento di un ciclo di vita della navigazione. –

2

A differenza di Thorsten Io non hanno dovuta usare SHDocVw, ma che cosa ha fatto fare la differenza per me è stato l'aggiunta del ciclo controllo ReadyState e l'utilizzo di applicazioni.DoEvents() mentre non è pronto. Ecco il mio codice:

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

E ho usato la soluzione di Yuki per la verifica dei risultati di WebBrowser_DocumentCompleted, anche se con l'ultima if/else scambiato per un commento dell'utente:

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

funzionato come un fascino :)

-1

Ho appena pensato di inserire una o due righe qui su un piccolo miglioramento che funziona in congiunzione con il codice di FeiBao. L'idea è di iniettare una variabile landmark (javascript) nella pagina web e usarla per rilevare quale degli eventi DocumentComplete successivi è il vero affare. Dubito che sia a prova di proiettile ma ha funzionato in modo più affidabile in generale rispetto all'approccio che manca. Qualsiasi commento benvenuto. Ecco il codice boilerplate:

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