Как использовать событие управления WebBrowser DocumentCompleted в C #? - PullRequest
14 голосов
/ 08 мая 2009

Перед тем, как начать писать этот вопрос, я пытался решить следующее

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

Проблема заключалась в том, что если на веб-странице существует iframe, событие DocumentCompleted будет запускаться более одного раза (после завершения каждого документа). Весьма вероятно, что программа попыталась прочитать данные из DOM, которые не были завершены и, естественно, потерпели неудачу.

Но вдруг во время написания этого вопроса «Что, если» монстр вдохновил меня, и я решил проблему, которую пытался решить. Поскольку мне не удалось это сделать из Google, я подумал, что было бы неплохо опубликовать это здесь.

    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
    }

Пока, по крайней мере, мой 5-метровый хак работает нормально.

Может быть, я действительно не могу запросить Google или MSDN, но я не могу найти: «Как использовать событие управления веб-браузером DocumentCompleted в C #?»

Примечание: Узнав много о web-контроле, я обнаружил, что он делает FuNKY.

Даже если вы обнаружите, что документ завершен, в большинстве случаев он не останется таким навсегда. Обновление страницы может быть выполнено несколькими способами - обновление кадра, ajax-подобный запрос или push на стороне сервера (необходимо иметь некоторый элемент управления, который поддерживает асинхронную связь и имеет взаимодействие html или JavaScript). Также некоторые фреймы никогда не будут загружаться, поэтому не стоит ждать их вечно.

В итоге я использовал:

if (e.Url != wb.Url)

Ответы [ 5 ]

14 голосов
/ 16 июня 2010

Возможно, вы также захотите знать вызовы AJAX.

Подумайте об этом:

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
    }
}
3 голосов
/ 13 октября 2009

Мне еще предстоит найти рабочее решение этой проблемы в Интернете. Надеюсь, это поможет достичь лучших результатов и сэкономит всем месяцы тонкой настройки, которые я потратил на его решение, и связанные с этим крайние случаи. Я боролся за эту проблему на протяжении многих лет, так как Microsoft изменила реализацию / надежность isBusy и document.readystate. С IE8 мне пришлось прибегнуть к следующему решению. Это похоже на вопрос / ответ от Маргуса с несколькими исключениями. Мой код будет обрабатывать вложенные фреймы, запросы javascript / ajax и мета-перенаправления. Я упростил код для ясности, но я также использую функцию тайм-аута (не входит в комплект) для сброса веб-страницы после 5 минут, когда domAccess по-прежнему равен 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;
    }
}
2 голосов
/ 02 февраля 2011

В отличие от Торстена, мне не нужно было использовать ShDocVw, но для меня было важно добавить цикл проверки ReadyState и использование Application.DoEvents (), пока он не готов. Вот мой код:

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

И я использовал решение Юки для проверки результатов WebBrowser_DocumentCompleted, хотя с последним обменом if / else на комментарий пользователя:

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

работал как шарм:)

0 голосов
/ 12 сентября 2011

Просто подумал, что здесь можно написать пару строк о небольшом улучшении, которое работает в сочетании с кодом FeiBao. Идея состоит в том, чтобы внедрить переменную ориентира (javascript) в веб-страницу и использовать ее для определения, какое из последующих событий DocumentComplete является реальной сделкой. Я сомневаюсь, что это пуленепробиваемый, но в целом он работал более надежно, чем подход, в котором его нет. Любые комментарии приветствуются. Вот стандартный код:

 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
        }
    }
0 голосов
/ 05 июня 2009

Я должен был сделать что-то подобное. Я использую ShDocVw напрямую (добавив ссылку на все необходимые сборки взаимодействия в мой проект). Затем я не добавляю элемент управления WebBrowser в мою форму, но AXShDocVw.AxWebBrowser .

Для навигации и ожидания я использую следующий метод:

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