HTML - Как узнать, когда загружаются все кадры?

Я использую элемент управления .NET WebBrowser. Как узнать, когда веб-страница полностью загружена?

Я хочу знать, когда браузер не получает больше данных. (Момент, когда IE пишет "Готово" в строке состояния...).

Примечания:

  • События DocumentComplete/NavigateComplete могут появляться несколько раз для веб-сайта, содержащего несколько фреймов.
  • Состояние готовности к браузеру также не решает проблему.
  • Я попытался проверить количество кадров в коллекции фреймов, а затем подсчитать количество раз, когда я получаю событие DocumentComplete, но это тоже не работает.
  • this.WebBrowser.IsBusy тоже не работает. Это всегда "ложно" при проверке в обработчике Document Complete.

Ответ 1

Вот что, наконец, помогло мне:

       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 loaded");
            return true;
        }
    }

В каждом завершении каждого документа я запускаю весь элемент html и проверяю все доступные фреймы (я знаю, что он может быть оптимизирован). Для каждого кадра я проверяю его состояние готовности. Он довольно надежный, но, как и jeffamaphone, я уже видел сайты, которые вызвали некоторые внутренние обновления. Но приведенный выше код удовлетворяет мои потребности.

Изменить: каждый кадр может содержать фреймы внутри него, поэтому я думаю, что этот код нужно обновить, чтобы рекурсивно проверить состояние каждого кадра.

Ответ 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, потому что он может срабатывать до того, как ваша страница даже начнет загружаться и преждевременно выполнит ваш код.

Ответ 3

Вот как я решил проблему в своем приложении:

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

Ответ 4

Здесь моя протестированная версия. Просто сделайте это 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 */
}

Ответ 5

Вы пробовали свойство WebBrowser.IsBusy?

Ответ 6

Как насчет использования javascript в каждом фрейме для установки флага, когда кадр завершен, а затем С# посмотреть на флаги?

Ответ 7

У меня нет альтернативы для вас, но мне интересно, является ли свойство IsBusy tru e во время обработчика Document Complete тем, что обработчик все еще работает, и поэтому элемент управления WebBrowser занят".

Простейшим решением было бы иметь цикл, который выполняется каждые 100 мс или около того, пока флаг IsBusy не будет reset (с максимальным временем выполнения в случае ошибок). Это, конечно, предполагает, что IsBusy не будет установлено на false в любой момент загрузки страницы.

Если обработчик Document Complete выполняется в другом потоке, вы можете использовать блокировку для отправки основного потока в режим сна и разбудить его из потока Complete Document. Затем проверьте флаг IsBusy, повторная блокировка основного потока - это все еще true.

Ответ 8

Я не уверен, что это сработает, но попробуйте добавить 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); 

Ответ 9

Можете ли вы использовать jQuery? Затем вы можете легко связать события готовности кадра на целевых кадрах. См. этот ответ для инструкций. Это сообщение в блоге также обсуждает его. Наконец, есть плагин, который вы могли бы использовать.

Идея состоит в том, что вы подсчитываете количество кадров на веб-странице, используя:

$("iframe").size()

а затем подсчитываете, сколько раз было запущено событие iframe.

Ответ 10

Вы получите событие BeforeNavigate и DocumentComplete для внешней веб-страницы, а также для каждого фрейма. Вы знаете, что все закончилось, когда вы получили событие DocumentComplete для внешней веб-страницы. Чтобы определить это, вы должны использовать управляемый эквивалент IWebBrowser2:: TopLevelContainer().

Остерегайтесь, однако, что сам веб-сайт может запускать больше кадровых навигаций в любое время, поэтому вы никогда не знаете, действительно ли страница действительно выполняется навсегда. Лучшее, что вы можете сделать, - это подсчитать количество всех ожидающих, которые вы видите, и уменьшить счетчик, когда вы получаете DocumentComplete.

Изменить: здесь управляемые документы: TopLevelContainer.

Ответ 11

Я просто использую метод webBrowser.StatusText. Когда он говорит "Готово", все загружается! Или я что-то упускаю?

Ответ 12

Проверка на IE.readyState = READYSTATE_COMPLETE должна работать, но если это не доказывает, что вам нужно, и вы буквально хотите знать "момент, когда IE пишет" Done "в своей строке состояния, тогда вы можете сделать цикл до тех пор, пока IE.StatusText содержит" Готово".