我正在尝试为我的程序找到一种方法来知道WebBrowser何时导航以及何时不导航。这是因为程序将通过JavaScript与加载的文档进行交互,而JavaScript将注入(inject)文档中。除了处理Navigating事件外,我没有其他方法可以知道何时开始导航,因为这不是我的程序,而是通过与文档进行交互进行导航的用户。但是,当DocumentCompleted
出现时,并不一定意味着它已经完成导航。我已经搜查了很多东西,发现了两个伪解决方案:
DocumentCompleted
事件中检查WebBrowser的ReadyState属性。这样做的问题是,如果不是加载文档而是加载文档中的框架,即使主文档未完成,ReadyState
也将是Completed
。 DocumentCompleted
的Url参数是否与WebBrowser
的Url相匹配。这样,我就会知道DocumentCompleted
没有被文档中的其他框架调用。 正如我所说,2的问题是,我唯一需要知道页面何时导航的方法是处理
Navigating
(或Navigated
)事件。因此,例如,如果我在Google map 中,然后单击“搜索”,则会调用Navigating
,但仅在导航中。而不是整个页面(在特定的Google案例中,我可以使用TargetFrameName
的WebBrowserNavigatingEventArgs
属性来检查它是不是正在导航的框架,但是框架并不总是具有名称)。因此,此后将调用DocumentCompleted
,但不会使用与我的Url
的WebBrowser
属性相同的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
重写方法上调用OnNavigated
,OnDocumentCompleted
和WebBrowser
方法。 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用于任何计数方法。