我正在尝试为我的程序找到一种方法来了解WebBrowser何时导航以及何时不导航。这是因为程序将通过将在文档中注入的JavaScript与加载的文档进行交互。我没有任何其他方式知道何时开始导航而不是处理Navigating事件,因为不是我的程序,而是通过与文档交互导航的用户。但是,当DocumentCompleted
发生时并不一定意味着它已完成导航。我一直在谷歌上搜索,发现了两个伪解决方案:
在DocumentCompleted
事件中检查WebBrowser的ReadyState属性。这样做的问题是,如果不是文档而是加载文档中的框架,即使主文档没有完成,ReadyState
也将是Completed
。
为防止出现这种情况,他们建议您查看传递给DocumentCompleted
的{{3}}参数是否与WebBrowser
的{{3}}相匹配。这样我就知道文档中的其他框架没有调用DocumentCompleted
。
2的问题在于,正如我所说,我必须知道页面导航的唯一方法是处理Navigating
(或Navigated
)事件。例如,如果我在Google地图中并点击搜索,则会调用Navigating
,但只是一个框架正在导航;不是整个页面(在特定的Google案例中,我可以使用TargetFrameName
的{{1}}属性来检查它是否是正在导航的帧,但帧并不总是有名称。那么在此之后,WebBrowserNavigatingEventArgs
将会被调用,但不会与DocumentCompleted
的{{1}}属性相同Url
,因为它只是一个导航的帧,所以我的程序将会永远导航它。
添加对WebBrowser
的通话并减去对Url
的通话也不会有效。它们并不总是一样的。我已经好几个月没有找到这个问题的解决方案;我一直在使用解决方案1和2,并希望它们适用于大多数情况。我的计划是使用计时器,以防某些网页出现错误或其他问题,但我认为Google地图没有任何错误。我仍然可以使用它,但唯一更丑陋的解决方案是烧毁我的电脑。
编辑:到目前为止,这是我最接近解决方案的地方:
Navigating
DocumentCompleted
继承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
会让我知道它是否正在导航。
答案 0 :(得分:4)
等到文档加载是一个难题,但是你想要不断检查.ReadyState和.Busy(不要忘记)。我会给你一些你需要的一般信息,然后我会在最后回答你的具体问题。
BTW,NC = NavigateComplete和DC = DocumentComplete。
此外,如果您正在等待的页面有帧,您需要获取它们并检查它们的.busy和.readystate,如果帧是嵌套的,则嵌套帧.readystate和.busy为好吧,所以你需要编写一个递归地检索那些引用的函数。
现在,无论它有多少帧,第一次触发的NC事件始终是顶级文档,最后一次触发的DC事件也始终是顶级(父级)文档。
所以你应该检查它是第一次调用还是pDisp Is WebBrowser1.object
(字面意思是你在if语句中键入的内容)然后你知道它的顶级文档的NC,然后你等待这个相同的对象要出现在DC事件中,请将pDisp保存到全局变量,并等待直到运行DC并且DC的pDisp等于您在第一次NC事件期间保存的全局pDisp(例如,您的pDisp)全局保存在第一个触发的NC事件中)。因此,一旦您知道pDisp在DC中返回,您就知道整个文档已完成加载。
这将改善你的当前方法,但是,为了使它更加简单,你需要进行帧检查,因为即使你做了以上所有,它超过90%好但不是100%傻瓜证明,需要为此做更多的事情。
为了以有意义的方式成功进行NC / DC计数(有可能,请相信我),您需要将每个NC的pDisp保存在数组或集合中,当且仅当它不存在时在该数组/集合中。完成这项工作的关键是检查重复的NC pDisp,如果存在则不添加它。因为发生的事情是,NC使用特定URL触发,然后发生服务器端重定向或URL更改,当发生这种情况时,NC再次被触发,但它发生在用于旧URL的相同pDisp对象上。因此,相同的pDisp对象将被发送到第二个NC事件,该事件现在第二次出现一个新的URL,但仍然完全使用完全相同的pDisp对象。
现在,因为你有一个所有唯一的NC pDisp对象的计数,你可以(逐个)删除它们,因为每个DC事件发生,通过执行典型的If pDisp Is pDispArray(i) Then
(这是在VB中)比较包裹一个For循环,并且对于每个取消,你的阵列数将接近0.这是准确的方法,但是,仅此一项是不够的,因为在你的计数达到0之后可能发生另一个NC / DC对此外,您必须记住在DC事件中执行与NavigateError事件完全相同的For Loop pDisp检查,因为当发生导航错误时,会触发DC事件的INSTEAD的NavigateError事件。
我知道这需要做很多事情,但是我花了多年的时间来处理这个可怕的控制来解决这些问题,我还有其他的代码&amp;方法,如果你需要,但我在这里提到的与WB Navigation真正准备好的一些东西,之前没有在线发布,所以我真的希望你发现它们很有用,让我知道你怎么做。此外,如果你想要/需要澄清一些这方面让我知道,不幸的是,如果你想100%确定网页已经完成加载,欢呼,上述并不是一切。
PS:另外,忘记提及,依赖URL来进行任何类型的计数是不准确的,这是一个非常糟糕的主意,因为几个帧可以有相同的URL - 例如,www.microsoft.com网站这样做,你可以在地址栏中看到3个左右的MS主站点。不要将URL用于任何计数方法。答案 1 :(得分:1)
不,没有适用于所有网站的方法。原因:Javascript可能会突然触发导航(想想AJAX ......)并且没有办法预测是否或何时发生这种情况。除非你当然为特定的网站开发。
我建议问一个不同的问题:如果您想要做某事时进行导航会怎样?一旦你知道你可以发现错误。
答案 2 :(得分:0)
首先我将文档转换为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是超时值。 我希望这会有所帮助。