如何在C#中使用WebBrowser控件DocumentCompleted事件?
在开始写这个问题之前,我试图解决以下问题如何在C#中使用WebBrowser控件DocumentCompleted事件?,c#,automation,c#-2.0,webbrowser-control,C#,Automation,C# 2.0,Webbrowser Control,在开始写这个问题之前,我试图解决以下问题 // 1. navigate to page // 2. wait until page is downloaded // 3. read and write some data from/to iframe // 4. submit (post) form 问题是,如果网页上存在iframe,DocumentCompleted事件将被多次触发(在每个文档完成之后)。程序很可能试图从DOM中读取未完成且自然失败的数据 但是在写这个问题的时候,突然间,
// 1. navigate to page
// 2. wait until page is downloaded
// 3. read and write some data from/to iframe
// 4. submit (post) form
问题是,如果网页上存在iframe,DocumentCompleted事件将被多次触发(在每个文档完成之后)。程序很可能试图从DOM中读取未完成且自然失败的数据
但是在写这个问题的时候,突然间,怪物启发了我,我解决了我试图解决的问题。当我在谷歌上搜索失败时,我想把它贴在这里会很好
private int iframe_counter = 1; // needs to be 1, to pass DCF test
public bool isLazyMan = default(bool);
/// <summary>
/// LOCK to stop inspecting DOM before DCF
/// </summary>
public void waitPolice() {
while (isLazyMan) Application.DoEvents();
}
private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e) {
if(!e.TargetFrameName.Equals(""))
iframe_counter --;
isLazyMan = true;
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
if (!((WebBrowser)sender).Document.Url.Equals(e.Url))
iframe_counter++;
if (((WebBrowser)sender).Document.Window.Frames.Count <= iframe_counter) {//DCF test
DocumentCompletedFully((WebBrowser)sender,e);
isLazyMan = false;
}
}
private void DocumentCompletedFully(WebBrowser sender, WebBrowserDocumentCompletedEventArgs e){
//code here
}
我不得不做类似的事情。我所做的是直接使用ShDocVw(向我的项目中添加对所有必要互操作程序集的引用)。然后,我不会将WebBrowser控件添加到表单中,而是添加AXShDocVw.AxWebBrowser控件 要导航并等待,我使用以下方法:
private void GotoUrlAndWait(AxWebBrowser wb, string url)
{
object dummy = null;
wb.Navigate(url, ref dummy, ref dummy, ref dummy, ref dummy);
// Wait for the control the be initialized and ready.
while (wb.ReadyState != SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
Application.DoEvents();
}
我还没有在网上找到解决这个问题的有效方法。希望这将使它的顶部,并节省每个人几个月的调整,我花了试图解决它,以及与之相关的边缘案件。多年来,随着微软改变了iBusy和document.readystate的实现/可靠性,我一直在为这个问题争论不休。对于IE8,我不得不求助于以下解决方案。这与Margus的问题/答案相似,只有少数例外。我的代码将处理嵌套帧、javascript/ajax请求和元重定向。为了清晰起见,我简化了代码,但如果5分钟后domcaccess仍然等于false,我还使用超时函数(不包括)重置网页
private void m_WebBrowser_BeforeNavigate(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
{
//Javascript Events Trigger a Before Navigate Twice, but the first event
//will contain javascript: in the URL so we can ignore it.
if (!URL.ToString().ToUpper().StartsWith("JAVASCRIPT:"))
{
//indicate the dom is not available
this.domAccess = false;
this.activeRequests.Add(URL);
}
}
private void m_WebBrowser_DocumentComplete(object pDisp, ref object URL)
{
this.activeRequests.RemoveAt(0);
//if pDisp Matches the main activex instance then we are done.
if (pDisp.Equals((SHDocVw.WebBrowser)m_WebBrowser.ActiveXInstance))
{
//Top Window has finished rendering
//Since it will always render last, clear the active requests.
//This solves Meta Redirects causing out of sync request counts
this.activeRequests.Clear();
}
else if (m_WebBrowser.Document != null)
{
//Some iframe completed dom render
}
//Record the final complete URL for reference
if (this.activeRequests.Count == 0)
{
//Finished downloading page - dom access ready
this.domAccess = true;
}
}
您可能还想知道AJAX调用 考虑使用以下方法:
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string url = e.Url.ToString();
if (!(url.StartsWith("http://") || url.StartsWith("https://")))
{
// in AJAX
}
if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
{
// IFRAME
}
else
{
// REAL DOCUMENT COMPLETE
}
}
与Thorsten不同,我不必使用ShDocVw,但对我来说,真正不同的是添加了循环检查ReadyState,并在未准备就绪时使用Application.DoEvents()。这是我的密码:
this.webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
foreach (var item in this.urlList) // This is a Dictionary<string, string>
{
this.webBrowser.Navigate(item.Value);
while (this.webBrowser1.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
}
工作起来很有魅力:)我想在这里写一两行关于飞豹代码的小改进。其想法是在网页中注入一个landmark(javascript)变量,并使用该变量来检测后续的DocumentComplete事件中哪些是真正的事件。我怀疑它是防弹的,但总的来说,它比缺乏它的方法更可靠。欢迎任何意见。以下是样板代码:
void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string url = e.Url.ToString();
var browser = (WebBrowser)sender;
if (!(url.StartsWith("http://") || url.StartsWith("https://")))
{
// in AJAX
}
if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
{
// IFRAME
}
else if (browser.Document != null && (bool)browser.Document.InvokeScript("eval", new object[] { @"typeof window.YourLandMarkJavascriptVariableHere === 'undefined'" }))
{
((IHTMLWindow2)browser.Document.Window.DomWindow).execScript("var window.YourLandMarkJavascriptVariableHere = true;");
// REAL DOCUMENT COMPLETE
// Put my code here
}
}
DocumentCompleted事件期间IsBusy属性的状态是什么?第一个帧准备就绪后,IsBusy将返回false。请注意,您的代码将无法处理多个正常帧。请注意,如果页面使用Ajax,它将失败,因为页面永远不会“完成”.你能详细说明一下与以前IE版本的区别吗?在我从事webbrowser自动化的早期,我能够简单地使用documentcomplete功能-大约是2002/3-ie5.5/6。如果您的pdisp对象与顶部文档窗口匹配,则文档已完全准备就绪,并且始终处于激发状态。现在~当~它真的启动时,您可以确信您的文档已经准备好了,但是您如何知道在异步事件模型中,如果没有大量无效超时,事件何时不会发生?IsBusy曾经是更改鼠标光标的一个很好的指示器,但我看到IsBusy在最新版本的7&8中无限期地保持为真。如果我没记错的话,AJAX请求直到IE6才正确触发这些事件,并且由于ie7或ie8,它们现在在导航事件之前触发重复事件。甚至不用担心导航完成或下载完成事件,因为它们不会帮助您确定导航生命周期的完成状态。+1,但else部分将包含真正的文档完成,其中if条件将是IFRAME
private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string url = e.Url.ToString();
var browser = (WebBrowser)sender;
if (!(url.StartsWith("http://") || url.StartsWith("https://")))
{
// in AJAX
}
if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
{
// IFRAME
}
else
{
// REAL DOCUMENT COMPLETE
// Put my code here
}
}
void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
string url = e.Url.ToString();
var browser = (WebBrowser)sender;
if (!(url.StartsWith("http://") || url.StartsWith("https://")))
{
// in AJAX
}
if (e.Url.AbsolutePath != this.webBrowser.Url.AbsolutePath)
{
// IFRAME
}
else if (browser.Document != null && (bool)browser.Document.InvokeScript("eval", new object[] { @"typeof window.YourLandMarkJavascriptVariableHere === 'undefined'" }))
{
((IHTMLWindow2)browser.Document.Window.DomWindow).execScript("var window.YourLandMarkJavascriptVariableHere = true;");
// REAL DOCUMENT COMPLETE
// Put my code here
}
}