我正在尝试为我的程序找到一种方法来知道WebBrowser何时导航以及何时不导航。这是因为程序将通过JavaScript与加载的文档进行交互,而JavaScript将注入(inject)文档中。除了处理Navigating事件外,我没有其他方法可以知道何时开始导航,因为这不是我的程序,而是通过与文档进行交互进行导航的用户。但是,当DocumentCompleted出现时,并不一定意味着它已经完成导航。我已经搜查了很多东西,发现了两个伪解决方案:

  • DocumentCompleted事件中检查WebBrowser的ReadyState属性。这样做的问题是,如果不是加载文档而是加载文档中的框架,即使主文档未完成,ReadyState也将是Completed
  • 为防止这种情况,他们建议查看传递给DocumentCompletedUrl参数是否与WebBrowserUrl相匹配。这样,我就会知道DocumentCompleted没有被文档中的其他框架调用。

  • 正如我所说,2的问题是,我唯一需要知道页面何时导航的方法是处理Navigating(或Navigated)事件。因此,例如,如果我在Google map 中,然后单击“搜索”,则会调用Navigating,但仅在导航中。而不是整个页面(在特定的Google案例中,我可以使用TargetFrameNameWebBrowserNavigatingEventArgs属性来检查它是不是正在导航的框架,但是框架并不总是具有名称)。因此,此后将调用DocumentCompleted,但不会使用与我的UrlWebBrowser属性相同的Url,因为它只是一个被导航的框架,所以我的程序将使它永远在导航。

    添加对Navigating的调用和减去对DocumentCompleted的调用也不起作用。它们并不总是相同的。我已经几个月没有找到解决这个问题的方法了;我一直在使用解决方案1和2,希望它们在大多数情况下都可以使用。我的计划是使用计时器,以防某些网页有错误或其他问题,但我认为Google Maps没有任何错误。我仍然可以使用它,但是唯一更糟糕的解决方案是烧毁我的PC。

    编辑:到目前为止,这是我最接近的解决方案:
    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重写方法上调用OnNavigatedOnDocumentCompletedWebBrowser方法。 IsSafeNavigating属性是一个让我知道它是否在导航的属性。

    最佳答案

    等到文档加载完毕是一个难题,但是您要不断检查.ReadyState和.Busy(请不要忘记)。我将为您提供一些所需的一般信息,然后在最后回答您的特定问题。

    顺便说一句,NC = NavigateComplete,DC = DocumentComplete。

    另外,如果您正在等待的页面具有框架,则需要获取它们的引用,并同时检查它们的.busy和.readystate,如果框架是嵌套的,则嵌套框架.readystate和.busy也是如此。您需要编写一个递归检索这些引用的函数。

    现在,无论帧有多少帧,第一个触发的NC事件始终是最顶层的文档,而最后一个触发的DC事件也始终是顶层(父)文档的最顶层文档。

    因此,您应该检查它是否是第一个调用和pDisp Is WebBrowser1.object(字面意思就是您在if语句中键入的内容),然后知道其顶层文档的NC,然后等待该对象出现在DC事件中,因此将pDisp保存到全局变量,然后等待直到运行DC,并且DC的pDisp等于您在第一个NC事件期间保存的Global pDisp(例如,在第一个NC事件中全局保存的pDisp)被解雇)。因此,一旦知道DC中返回了pDisp,就知道整个文档已完成加载。

    这将改善您的当前方法,但是,要使其更加简单,您还需要进行框架检查,因为即使您已完成上述所有操作,它仍然可以达到90%的良好效果,但并非100%的简单性证明为此做更多。

    为了以有意义的方式成功进行NC/DC计数(可能,相信我),您需要将每个NC的pDisp保存在数组或集合中(当且仅当该数组中不存在它时)/采集。进行这项工作的关键是检查重复的NC pDisp,如果存在则不添加它。因为发生的情况是,NC会使用特定的URL触发,然后会发生服务器端重定向或URL更改,并且在发生这种情况时,会再次触发NC,但是使用与旧URL相同的pDisp对象会发生NC。因此,将同一个pDisp对象发送到第二个NC事件,该事件第二次使用新的URL发生,但是仍然使用完全相同的pDisp对象完成所有操作。

    现在,由于您拥有所有唯一的NC pDisp对象的计数,因此可以通过在For循环中包装典型的If pDisp Is pDispArray(i) Then(在VB中)进行比较,并针对每个DC事件(一次)删除它们。取一个,您的数组计数将接近于0。这是准确的方法,但是,仅此一项是不够的,因为在计数达到0后,可能会出现另一个NC/DC对。另外,您还必须记住与在DC事件中一样,在NavigateError事件中执行完全相同的For Loop pDisp检查,因为发生导航错误时,将在DC事件的INSTEAD上触发NavigateError事件。

    我知道这是很多事情,但是花了我很多年的时间才能处理这个可怕的控件,以弄清这些事情,如果需要的话,我还有其他代码和方法,但是我在这里提到的一些相关内容WB导航已经真正准备好了,以前还没有在线发布过,所以我真的希望您发现它们有用,并告诉我您的情况。另外,如果您想/需要澄清其中的一些内容,请告诉我,不幸的是,如果您想百分百确定网页已完成加载,那么以上内容并不代表一切,请加油。

    PS:另外,忘了提及,依靠URL进行任何计数都是不准确的,这是一个非常糟糕的主意,因为多个框架可以具有相同的URL-例如,www.microsoft.com网站这样做,就像3帧左右调用您在地址栏中看到的MS主站点。请勿将URL用于任何计数方法。

    07-24 17:56