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的主要内容,如果未能解决你的问题,请参考以下文章

表单完成提交后执行 javascript

异步操作执行后子页面重新修改父页面iframe高度

在 iframe 中提交后重新加载父页面

使用 Javascript 更改 iframe src

iframe-resizer JS 库:子锚链接不工作 w。 iFrame 父级

谷歌浏览器在 JavaScript 动态更改后未更新 iframe 元素 innerHTML