我需要捕获生成的HTML图像。我在这里使用Alex Filipovici的优秀解决方案:Convert HTML string to image。除非我正在尝试加载包含使用某些Javascript加载的iframe的页面,否则它的效果很好。
static int width = 1024; static int height = 768; public static void Capture() { var html = @" <!DOCTYPE html> <meta http-equiv='X-UA-Compatible' content='IE=Edge'> <html> <iframe id='forecast_embed' type='text/html' frameborder='0' height='245' width='100%' src='http://forecast.io/embed/#lat=42.3583&lon=-71.0603&name=Downtown Boston'> </iframe> </html> "; StartBrowser(html); } private static void StartBrowser(string source) { var th = new Thread(() => { var webBrowser = new WebBrowser(); webBrowser.Width = width; webBrowser.Height = height; webBrowser.ScrollBarsEnabled = false; webBrowser.DocumentCompleted += webBrowser_DocumentCompleted; webBrowser.DocumentText = source; Application.Run(); }); th.SetApartmentState(ApartmentState.STA); th.Start(); } static void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) { var webBrowser = (WebBrowser)sender; using (Bitmap bitmap = new Bitmap(width, height)) { webBrowser.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, width, height)); bitmap.Save(@"image.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); } Application.Exit(); }
据我所知,可能没有明确的方法可以知道所有的javascript是否已经结束以及iframe加载的变幻莫测以及DocumentCompleted被调用的事实多次,因为有帧/ iframes + 1.我可以处理iframe加载使用计数器或其他东西,但我想要的是一个合理的延迟,所以加载了javascript,我没有得到像这样的“正在加载”的图像:http://imgur.com/FiFMTmm
答案 0 :(得分:3)
如果您正在处理使用框架和AJAX的动态网页,那么在特定网页完成加载资源时找不到完美的解决方案。你可以通过以下两件事来接近:
window.onload
事件; WebBrowser
Busy属性,并预先设定一些相当短的超时时间。 例如,(查看https://stackoverflow.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
,可以使用计时器实现相同的逻辑。
答案 1 :(得分:0)
以下是我经常使用的各种其他想法,这些想法最终变得复杂并且有竞争条件或需要.Net 4.5(例如这个问题的答案)。
诀窍是在每个DocumentCompleted上重新启动秒表并等到某个阈值内没有完成任何文档。
为了更容易使用,我加入了一个扩展方法:
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;
}
}
}
}