Можно ли точно знать, перемещается веб-браузер или нет? - PullRequest
7 голосов
/ 08 ноября 2010

Я пытаюсь найти способ, чтобы моя программа знала, когда веб-браузер перемещается, а когда нет.Это связано с тем, что программа будет взаимодействовать с загруженным документом через JavaScript, который будет вставлен в документ.У меня нет другого способа узнать, когда он начинает навигацию, кроме обработки события Navigating , поскольку это не моя программа, а пользователь, который будет перемещаться, взаимодействуя с документом.Но тогда, когда DocumentCompleted происходит, это не обязательно означает, что он закончил навигацию.Я много гуглял и нашел два псевдо-решения:

  1. Проверьте свойство ReadyState WebBrowser в событии DocumentCompleted.Проблема в том, что если загружается не документ, а рамка документа, ReadyState будет Completed, даже если основной документ не будет завершен.

  2. Для предотвращенияпри этом они советуют проверить, соответствует ли параметр Url , переданный в DocumentCompleted, Url из WebBrowser.Таким образом, я бы знал, что DocumentCompleted не вызывается каким-либо другим фреймом в документе.

Проблема с 2 заключается в том, что, как я уже сказал, единственный способ, которым я должензнать, когда страница перемещается, обрабатывая событие Navigating (или Navigated).Так, если, например, я нахожусь в Картах Google и нажимаю Поиск, будет вызываться Navigating, но перемещается только рамка;не всю страницу (в конкретном случае Google, я мог бы использовать свойство TargetFrameName WebBrowserNavigatingEventArgs, чтобы проверить, является ли фрейм тем, который перемещается, но у фреймов не всегда есть имена).Таким образом, после этого будет вызван DocumentCompleted, но не с тем же свойством Url, что и у моего свойства WebBrowser s Url, потому что это был только фрейм, по которому осуществлялась навигация, поэтому моя программа заметит, что она все еще перемещаетсянавсегда.

Добавление вызовов к Navigating и вычитание вызовов к DocumentCompleted также не будут работать.Они не всегда одинаковы.Я не нашел решения этой проблемы уже несколько месяцев;Я использую решения 1 и 2 и надеюсь, что они будут работать в большинстве случаев.Я планировал использовать таймер на случай, если на какой-либо веб-странице есть ошибки или что-то подобное, но я не думаю, что в Google Maps есть какие-либо ошибки.Я все еще мог бы использовать его, но единственным уродливым решением было бы сжечь мой компьютер.

Редактировать: Пока что это самое близкое решение, которое у меня есть:

partial class SafeWebBrowser
{
    private class SafeNavigationManager : INotifyPropertyChanged
    {
        private SafeWebBrowser Parent;
        private bool _IsSafeNavigating = false;
        private int AccumulatedNavigations = 0;
        private bool NavigatingCalled = false;

        public event PropertyChangedEventHandler PropertyChanged;

        public bool IsSafeNavigating
        {
            get { return _IsSafeNavigating; }
            private set { SetIsSafeNavigating(value); }
        }

        public SafeNavigationManager(SafeWebBrowser parent)
        {
            Parent = parent;
        }

        private void SetIsSafeNavigating(bool value)
        {
            if (_IsSafeNavigating != value)
            {
                _IsSafeNavigating = value;
                OnPropertyChanged(new PropertyChangedEventArgs("IsSafeNavigating"));
            }
        }

        private void UpdateIsSafeNavigating()
        {
            IsSafeNavigating = (AccumulatedNavigations != 0) || (NavigatingCalled == true);
        }

        private bool IsMainFrameCompleted(WebBrowserDocumentCompletedEventArgs e)
        {
            return Parent.ReadyState == WebBrowserReadyState.Complete && e.Url == Parent.Url;
        }

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null) PropertyChanged(this, e);
        }

        public void OnNavigating(WebBrowserNavigatingEventArgs e)
        {
            if (!e.Cancel) NavigatingCalled = true;
            UpdateIsSafeNavigating();
        }

        public void OnNavigated(WebBrowserNavigatedEventArgs e)
        {
            NavigatingCalled = false;
            AccumulatedNavigations++;
            UpdateIsSafeNavigating();
        }

        public void OnDocumentCompleted(WebBrowserDocumentCompletedEventArgs e)
        {
            NavigatingCalled = false;
            AccumulatedNavigations--;
            if (AccumulatedNavigations < 0) AccumulatedNavigations = 0;
            if (IsMainFrameCompleted(e)) AccumulatedNavigations = 0;
            UpdateIsSafeNavigating();
        }
    }
}

SafeWebBrowser наследуется WebBrowser.Методы OnNavigating, OnNavigated и OnDocumentCompleted вызываются для соответствующих переопределенных методов WebBrowser.Свойство IsSafeNavigating - это то, что сообщит мне, если оно перемещается или нет.

Ответы [ 3 ]

4 голосов
/ 11 февраля 2012

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

Кстати, NC = NavigateComplete и DC = DocumentComplete.

Кроме того, если выожидают наличия фреймов, вам нужно получить ссылку на них и проверить их .busy и .readystate, а если фреймы вложенные, то и вложенные фреймы .readystate и .busy, так что вам нужно написать функциюкоторый рекурсивно получает эти ссылки.

Теперь, независимо от того, сколько кадров у него есть, первое запущенное событие NC всегда является верхним документом, а последнее запущенное событие DC также всегда является событием верхнего (родительского) документа.

Таким образом, вы должны проверить, является ли это первый вызов, и pDisp Is WebBrowser1.object (буквально это то, что вы вводите в своем операторе if), тогда вы знаете, что это NC для документа верхнего уровня, затем вы ждете того же самогообъект должен появиться в событии DC, поэтому сохраните pDisp в глобальной переменной и подождите, пока DC не будет запущен, а pDisp этого DC равен GlobalpDisp, который вы сохранили во время первого события NC (например, pDisp, который вы сохранили глобально в первом событии NC, которое сработало).Поэтому, как только вы узнаете, что pDisp был возвращен в контроллере домена, вы знаете, что весь документ завершил загрузку.

Это улучшит ваш метод currect, однако, чтобы сделать его более надежным, вам нужно выполнить проверку фреймовтакже, поскольку, даже если вы сделали все вышеперечисленное, это более чем на 90% хорошо, но не на 100% надежно, нужно сделать больше для этого.

Для успешного подсчета NC / DC в значимом значенииКстати (возможно, поверьте мне) вам нужно сохранить pDisp каждого NC в массиве или коллекции, если и только если он еще не существует в этом массиве / коллекции.Ключом к выполнению этой работы является проверка дубликата NC pDisp, а не добавление его, если он существует.Поскольку происходит то, что NC запускается с определенным URL, затем происходит перенаправление на стороне сервера или изменение URL, и когда это происходит, NC запускается снова, НО это происходит с тем же объектом pDisp, который использовался для старого URL.Таким образом, тот же объект pDisp отправляется во второе событие NC, которое происходит во второй раз с новым URL-адресом, но все еще выполняется с точно таким же объектом pDisp.

Теперь, поскольку у вас есть счетчик всех уникальныхNC pDisp объекты, вы можете (один за другим) удалять их при каждом событии DC, выполняя типичное сравнение If pDisp Is pDispArray(i) Then (это в VB), заключенное в цикл For, и для каждого снятого счетчик вашего массива будетприблизиться к 0. Это точный способ сделать это, однако, одного этого недостаточно, так как другая пара NC / DC может появиться после того, как ваш счет достигнет 0. Кроме того, вы должны помнить, что нужно делать то же самое Для цикла L pDispпроверка в событии NavigateError, как и в событии DC, потому что при возникновении ошибки навигации происходит событие NavigateError ВМЕСТО события DC.

Я знаю, что это было много, но мне потребовалосьгоды, когда мне приходилось иметь дело с этим ужасным контролем, чтобы понять эти вещи, у меня есть другой код и методы, если вам нужно, но некоторые вещи, которые я упоминаюЗдесь, в связи с тем, что WB Navigation действительно готовы, ранее не публиковались в Интернете, поэтому я очень надеюсь, что вы найдете их полезными и дадите мне знать, как у вас дела.Кроме того, если вы хотите / нуждаетесь в разъяснениях по этому поводу, дайте мне знать, к сожалению, вышесказанное - это еще не все, если вы хотите быть на 100% уверены, что веб-страница загружена, ура.

PS: Такжезабыл упомянуть, что полагаться на URL-адреса для любого рода подсчета неточно, и это очень плохая идея, потому что несколько фреймов могут иметь один и тот же URL-адрес - например, на веб-сайте www.microsoft.com это делается как 3 фрейма илитак позвонив на основной сайт MS, который вы видите в адресной строке.Не используйте URL для любого метода подсчета.

1 голос
/ 12 ноября 2010

Нет, не существует метода, который будет работать для всех сайтов.Причина: Javascript может вызвать внезапную навигацию (подумайте AJAX ...), и нет способа предсказать, произойдет ли это или когда это произойдет.Конечно, если вы не разрабатываете для конкретного сайта.

Я рекомендую задать другой вопрос: что произойдет, если навигация происходит, когда вы хотите что-то сделать?Как только вы узнаете, что можете поймать ошибку.

0 голосов
/ 09 ноября 2010

Сначала я преобразовал документ в XML, а затем использовал мой магический метод:

    nodeXML = HtmlToXml.ConvertToXmlDocument((IHTMLDocument2)htmlDoc.DomDocument);
    if (ExitWait(false))
        return false;

преобразование:

public static XmlNode ConvertToXmlDocument(IHTMLDocument2 doc2)
{
    XmlDocument xmlDoc = new XmlDocument();
    IHTMLDOMNode htmlNodeHTML = null;
    XmlNode xmlNodeHTML = null;

    try
    {
        htmlNodeHTML = (IHTMLDOMNode)((IHTMLDocument3)doc2).documentElement;
        xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", ""/*((IHTMLDocument2)htmlDoc.DomDocument).charset*/, ""));
        xmlNodeHTML = xmlDoc.CreateElement("html"); // create root node
        xmlDoc.AppendChild(xmlNodeHTML);
        CopyNodes(xmlDoc, xmlNodeHTML, htmlNodeHTML);
    }
    catch (Exception err)
    {
        Utils.WriteLog(err, "Html2Xml.ConvertToXmlDocument");
    }

магический метод:

private bool ExitWait(bool bDelay)
{
    if (m_bStopped)
        return true;
    if (bDelay)
    {
        DateTime now = DateTime.Now;
        DateTime later = DateTime.Now;
        TimeSpan difT = (later - now);
        while (difT.TotalMilliseconds < MainDef.IE_PARSER_DELAY)
        {
            Application.DoEvents();
            System.Threading.Thread.Sleep(10);
            later = DateTime.Now;
            difT = later - now;
            if (m_bStopped)
                return true;
        }
    }
    return m_bStopped;
}

где m_bStopped равно false по умолчанию, IE_PARSER_DELAY является значением тайм-аута. Надеюсь, это поможет.

...