如何在 C# 中使用 WebBrowser 控件 DocumentCompleted 事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/840813/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use WebBrowser control DocumentCompleted event in C#?
提问by Margus
Before starting writing this question, i was trying to solve following
在开始写这个问题之前,我试图解决以下问题
// 1. navigate to page
// 2. wait until page is downloaded
// 3. read and write some data from/to iframe
// 4. submit (post) form
The problem was, that if a iframe exists on a web page, DocumentCompleted event would get fired more then once (after each document has been completed). It was highly likely that program would have tried to read data from DOM that was not completed and naturally - fail.
问题是,如果网页上存在 iframe,则 DocumentCompleted 事件将被触发多次(在每个文档完成后)。程序很可能会尝试从未完成的 DOM 读取数据,并且自然会失败。
But suddenly while writing this question 'What if' monsterinspired me, and i fix'ed the problem, that i was trying to solve. As i failed Google'ing this, i thought it would be nice to post it here.
但是突然在写这个问题“如果”怪物启发了我,我解决了我试图解决的问题。由于我在谷歌上失败了,我认为把它张贴在这里会很好。
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
}
For now at least, my 5m hack seems to be working fine.
至少现在,我的 5m hack 似乎工作正常。
Maybe i am really failing at querying google or MSDN, but i can not find: "How to use webbrowser control DocumentCompleted event in C# ?"
也许我在查询 google 或 MSDN 时真的很失败,但我找不到:“如何在 C# 中使用 webbrowser 控制 DocumentCompleted 事件?”
Remark:After learning a lot about webcontrol, I found that it does FuNKY stuff.
备注:在学习了很多关于 webcontrol 的知识后,我发现它做了一些 FuNKY 的东西。
Even if you detect that the document has completed, in most cases it wont stay like that forever. Page update can be done in several ways - frame refresh, ajax like request or server side push (you need to have some control that supports asynchronous communication and has html or JavaScript interop). Also some iframes will never load, so it's not best idea to wait for them forever.
即使您检测到文档已完成,在大多数情况下它也不会永远保持这样。页面更新可以通过多种方式完成 - 帧刷新、ajax 之类的请求或服务器端推送(您需要一些支持异步通信并具有 html 或 JavaScript 互操作的控件)。还有一些 iframe 永远不会加载,所以永远等待它们并不是最好的主意。
I ended up using:
我最终使用了:
if (e.Url != wb.Url)
采纳答案by Yuki
You might want to know the AJAX calls as well.
您可能还想知道 AJAX 调用。
Consider using this:
考虑使用这个:
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
}
}
回答by Thorsten Dittmar
I had to do something similar. What I do is use ShDocVw directly (adding a reference to all the necessary interop assemblies to my project). Then, I do not add the WebBrowser control to my form, but the AXShDocVw.AxWebBrowsercontrol.
我不得不做类似的事情。我所做的是直接使用 ShDocVw(将所有必要的互操作程序集的引用添加到我的项目中)。然后,我没有将 WebBrowser 控件添加到我的表单中,而是将AXShDocVw.AxWebBrowser控件添加到我的表单中。
To navigate and wait I use to following method:
要导航和等待,我使用以下方法:
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();
}
回答by Marcus Pope
I have yet to find a working solution to this problem online. Hopefully this will make it to the top and save everyone the months of tweaking I spent trying to solve it, and the edge cases associated with it. I have fought over this issue over the years as Microsoft has changed the implementation/reliability of isBusy and document.readystate. With IE8, I had to resort to the following solution. It's similar to the question/answer from Margus with a few exceptions. My code will handle nested frames, javascript/ajax requests and meta-redirects. I have simplified the code for clarity sake, but I also use a timeout function (not included) to reset the webpage after if 5 minutes domAccess still equals false.
我还没有在网上找到解决这个问题的有效方法。希望这将使它达到顶峰,并为每个人节省我为解决它而花费的几个月的调整时间,以及与之相关的边缘情况。多年来,随着 Microsoft 更改了 isBusy 和 document.readystate 的实现/可靠性,我一直在为这个问题而争论不休。对于 IE8,我不得不求助于以下解决方案。它类似于 Margus 的问题/答案,但有一些例外。我的代码将处理嵌套框架、javascript/ajax 请求和元重定向。为了清楚起见,我简化了代码,但如果 5 分钟 domAccess 仍然等于 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;
}
}
回答by u501262
Unlike Thorsten I didn't have to use ShDocVw, but what did make the difference for me was adding the loop checking ReadyState and using Application.DoEvents() while not ready. Here is my code:
与 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();
}
}
And I used Yuki's solution for checking the results of WebBrowser_DocumentCompleted, though with the last if/else swapped per user's comment:
我使用 Yuki 的解决方案来检查 WebBrowser_DocumentCompleted 的结果,尽管根据用户的评论交换了最后一个 if/else:
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
}
}
Worked like a charm :)
像魅力一样工作:)
回答by XDS
Just thought to drop a line or two here about a small improvement which works in conjunction with the code of FeiBao. The idea is to inject a landmark (javascript) variable in the webpage and use that to detect which of the subsequent DocumentComplete events is the real deal. I doubt it's bulletproof but it has worked more reliably in general than the approach that lacks it. Any comments welcome. Here is the boilerplate code:
只是想在这里放下一两行关于与飞宝代码结合使用的小改进。这个想法是在网页中注入一个地标 (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
}
}