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 содержит" Готово".