2013-10-15 79 views
2

我需要捕獲生成的HTML圖像。我從這裏使用亞歷克斯菲利波維奇的優秀解決方案:Convert HTML string to image。除了當我試圖加載一個帶有使用一些Javascript加載的iframe的頁面時,它工作得很好。WebBrowser控件文檔完成iframe和Javascript完成後

 
     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裝載的變幻莫測和事實,即有幀/ I幀+ 1。我可以處理DocumentCompleted得到的稱爲多次與iframe加載計數器或東西,但我想要的是一個合理的延遲,所以JavaScript加載,我沒有像這樣的「加載」的圖像:http://imgur.com/FiFMTmm

回答

3

如果你是處理大量使用框架和AJAX的動態網頁時,沒有完美的解決方案來查找特定頁面何時完成加載資源。您可以通過執行以下兩件事來關閉:

  • 處理頁面的window.onload事件;
  • 然後異步輪詢WebBrowserBusy屬性,帶有一些預定義的合理短暫超時。

例如,(檢查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,可以實現使用計時器相同的邏輯。

+0

你會在我的代碼中添加一個計時器嗎?簡單的延遲對我來說就足夠了。 – naveed

+0

@naveed,您可以將'webBrowser_DocumentCompleted'事件處理程序的簽名更改爲'async static void webBrowser_DocumentCompleted ...'。然後在'webBrowser_DocumentCompleted'內添加'await Task.Delay(1000)'作爲第一行。或者,如果沒有'async/await',請在'webBrowser_DocumentCompleted'內創建一個計時器,並將所有邏輯從'webBrowser_DocumentCompleted'移到計時器的事件處理程序中。有一件事要注意,無論如何,**'DocumentCompleted'可以被同一文檔多次觸發**(因爲框架)。使用靜態標誌變量來緩解這一點。 – Noseratio

+0

太棒了!這工作!感謝Noseratio。 – naveed

0

下面是我經歷了很多與各種其他想法混雜在一起後,使用結束了複雜和競爭條件或需要。淨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; 
      } 
     } 
    } 
}