iframe 和 Javascript 完成后 WebBrowser Control DocumentCompleted
Posted
技术标签:
【中文标题】iframe 和 Javascript 完成后 WebBrowser Control DocumentCompleted【英文标题】:WebBrowser Control DocumentCompleted after iframe & Javascript completion 【发布时间】:2013-10-22 16:57:42 【问题描述】:我需要捕获生成的 html 的图像。我在这里使用 Alex Filipovici 的出色解决方案:Convert HTML string to image。它工作得很好,除非我尝试加载具有使用某些 javascript 加载的 iframe 的页面。
静态 int 宽度 = 1024; 静态 int 高度 = 768; 公共静态无效捕获() 变量 html = @" "; 开始浏览器(html); 私有静态无效StartBrowser(字符串源) var th = 新线程(() => var webBrowser = new WebBrowser(); webBrowser.Width = 宽度; webBrowser.Height = 高度; webBrowser.ScrollBarsEnabled = false; webBrowser.DocumentCompleted += webBrowser_DocumentCompleted; webBrowser.DocumentText = 源; 应用程序.运行(); ); th.SetApartmentState(ApartmentState.STA); th.Start(); 静态无效 webBrowser_DocumentCompleted(对象发送者,WebBrowserDocumentCompletedEventArgs e) var webBrowser = (WebBrowser)sender; 使用(位图位图 = 新位图(宽度,高度)) webBrowser.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, width, height)); bitmap.Save(@"image.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); 应用程序.Exit();我知道可能没有明确的方法可以知道所有 javascript 是否已结束,iframe 加载的变幻莫测以及 DocumentCompleted get 的调用次数与帧/iframe + 1 一样多。我可以处理 iframe 加载有一个计数器或其他东西,但我想要的是一个合理的延迟,所以加载了 javascript,我没有得到像这样的“加载”的图像:http://imgur.com/FiFMTmm
【问题讨论】:
【参考方案1】:如果您正在处理大量使用框架和 AJAX 的动态网页,则没有完美的解决方案可以找到特定页面何时完成加载资源。你可以通过做以下两件事来接近:
处理页面的window.onload
事件;
然后异步轮询WebBrowser
Busy 属性,并带有一些预定义的合理短超时。
例如,(查看https://***.com/a/19283143/1768303 以获取完整示例):
const int AJAX_DELAY = 2000; // non-deterministic wait for AJAX dynamic code
const int AJAX_DELAY_STEP = 500;
// wait until webBrowser.Busy == false or timed out
async Task<bool> AjaxDelay(CancellationToken ct, int timeout)
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
cts.CancelAfter(timeout);
while (true)
try
await Task.Delay(AJAX_DELAY_STEP, cts.Token);
var busy = (bool)this.webBrowser.ActiveXInstance.GetType().InvokeMember("Busy", System.Reflection.BindingFlags.GetProperty, null, this.webBrowser.ActiveXInstance, new object[] );
if (!busy)
return true;
catch (OperationCanceledException)
if (cts.IsCancellationRequested && !ct.IsCancellationRequested)
return false;
throw;
如果您不想使用async/await
,您可以使用计时器实现相同的逻辑。
【讨论】:
您会在我的代码中的何处添加计时器?对我来说,一个简单的延迟就足够了。 @naveed,您可以将webBrowser_DocumentCompleted
事件处理程序的签名更改为async static void webBrowser_DocumentCompleted...
。然后在webBrowser_DocumentCompleted
中添加await Task.Delay(1000)
作为第一行。或者,不使用async/await
,在webBrowser_DocumentCompleted
中创建一个计时器,并将所有逻辑从webBrowser_DocumentCompleted
移到计时器的事件处理程序中。在任何情况下都需要注意一件事,DocumentCompleted
可以为同一个文档多次触发(因为帧)。使用静态标志变量来缓解这种情况。
没问题,很高兴它有帮助。【参考方案2】:
这是我在与各种其他想法混在一起之后一直在使用的东西,这些想法最终变得复杂并且有竞争条件或需要 .Net 4.5(例如这个问题的答案)。
诀窍是在每个 DocumentCompleted 上重新启动 Stopwatch 并等待直到在某个阈值内没有完成任何文档。
为了方便使用我加入了一个扩展方法:
browser.NavigateAndWaitUntilComplete(uri);
我应该将它命名为 NavigateUntilProbablyComplete()。这种方法的缺点是每次导航都会有 250 毫秒的延迟。我见过的许多解决方案都依赖于最终页面与我的场景中无法保证的 url 相同。
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace MyProject.Extensions
public static class WebBrowserExtensions
const int CompletionDelay = 250;
private class WebBrowserCompletionHelper
public Stopwatch LastCompletion;
public WebBrowserCompletionHelper()
// create but don't start.
LastCompletion = new Stopwatch();
public void DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
WebBrowser browser = sender as WebBrowser;
if (browser != null)
LastCompletion.Restart();
public static void NavigateAndWaitUntilComplete(this WebBrowser browser, Uri uri)
WebBrowserCompletionHelper helper = new WebBrowserCompletionHelper();
try
browser.DocumentCompleted += helper.DocumentCompleted;
browser.Navigate(uri);
Thread.Sleep(CompletionDelay);
Application.DoEvents();
while (browser.ReadyState != WebBrowserReadyState.Complete && helper.LastCompletion.ElapsedMilliseconds < CompletionDelay)
Thread.Sleep(CompletionDelay);
Application.DoEvents();
finally
browser.DocumentCompleted -= helper.DocumentCompleted;
【讨论】:
以上是关于iframe 和 Javascript 完成后 WebBrowser Control DocumentCompleted的主要内容,如果未能解决你的问题,请参考以下文章