Wednesday, September 15, 2010

How to take a snapshot of the a current aspx page using WebBrowser

The idea is to use System.Windows.Forms.WebBrowser control and get a snapshot of the working page. I have developed a class called Thumbnail which we can use to create a snapshot providing a URL or html text. I use html text mode in there. I provide html text plus the method as html. In the constructor it initializes the object.
Thumbnail thumbnail = new Thumbnail(html, 800, 600, width, height, Thumbnail.ThumbnailMethod.Html);
Then the GenerateThumbnail method is the most important bit. However in-order to use WebBrowser control in a web page (MTA – Multi Threaded Apartment) we have to execute the WebBrowser control in a STA (Single Thread Apartment). For this I create a separate thread a set apartment state to STA.
public Bitmap GenerateThumbnail()
{
    Thread thread = new Thread(new ThreadStart(GenerateThumbnailInteral));
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();
    return ThumbnailImage;
}
In the thread start, as the internal method I use GenerateThumbnailInternal method. In that method, I assign an event handler to the web browser object’s DocumentCompleted method. But the problem is it takes a while to web browser complete the request. By that time page finishes the execution. So we have to delay the page execution until WebBrowser completes the request. For this I use Application.DoEvents() method. I put the execution in a loop while WebBrowser is not completed its navigation.
private void GenerateThumbnailInteral()
{
    WebBrowser webBrowser = new WebBrowser();
    webBrowser.ScrollBarsEnabled = false;
    if (this.Method == ThumbnailMethod.Url)
        webBrowser.Navigate(this.Url);
    else webBrowser.DocumentText = this.Html;
    webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
    while (webBrowser.ReadyState != WebBrowserReadyState.Complete) Application.DoEvents();
    webBrowser.Dispose();
}
Then in the DocumentCompleted method, I create the bitmap using WebBrowser’s DrawToBmp(...) method.
///
/// this.ThumbnailImage is a BitMap object
/// 
this.ThumbnailImage = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height);
webBrowser.BringToFront();
webBrowser.DrawToBitmap(ThumbnailImage, webBrowser.Bounds);
this.ThumbnailImage = (Bitmap)ThumbnailImage.GetThumbnailImage(Width, Height, nullIntPtr.Zero);

Putting all together
Markup:
<%@ Page Language="C#" CodeBehind="~/Test.aspx.cs" Inherits="ActiveTest.Test" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:TextBox ID="txtWebsiteAddress" runat="server" Text="www.google.com" /> 
        <asp:Button ID="btnCreateThumbnailImage" runat="server" 
            Text="Create Thumbnail Image" OnClick="CreateThumbnailImage" /></td>
        <asp:TextBox ID="txtWidth" runat="server" Text="200" />
        <asp:TextBox ID="txtHeight" runat="server" Text="200" />
        <asp:Image ID="imgThumbnailImage" runat="server" Visible="false" />
    </div>
    </form>
</body>
</html>

Code:
public partial class Test : Page
{
    bool snap = false;
    protected void CreateThumbnailImage(object sender, EventArgs e)
    {            
        snap = true;
    }
    protected override void Render(HtmlTextWriter writer)
    {
        if (snap)
        {
            int width = Int32.Parse(txtWidth.Text);
            int height = Int32.Parse(txtHeight.Text);
            StringBuilder builder = new StringBuilder();
            HtmlTextWriter htw = new HtmlTextWriter(new StringWriter(builder));
            base.Render(htw);
            string html = builder.ToString();
            Thumbnail thumbnail = new Thumbnail(html, 800, 600, width, height, Thumbnail.ThumbnailMethod.Html);
            Bitmap image = thumbnail.GenerateThumbnail();
            image.Save(Server.MapPath("~") + "/Thumbnail.bmp");                
            writer.Write(html);
            writer.Write("<img src=\"Thumbnail.bmp\" />");
        }
        else
            base.Render(writer);
    }
}
public class Thumbnail
{
    public enum ThumbnailMethod { Url, Html };
    public string Url { getset; }
    public Bitmap ThumbnailImage { getset; }
    public int Width { getset; }
    public int Height { getset; }
    public int BrowserWidth { getset; }
    public int BrowserHeight { getset; }
    public string Html { getset; }
    public ThumbnailMethod Method { getset; }
 
    public Thumbnail(string data, int browserWidth, int browserHeight, int thumbnailWidth, 
                                    int thumbnailHeight, ThumbnailMethod method)
    {
        this.Method = method;
        if (method == ThumbnailMethod.Url)
            this.Url = data;
        else if (method == ThumbnailMethod.Html)
            this.Html = data;
        this.BrowserWidth = browserWidth;
        this.BrowserHeight = browserHeight;
        this.Height = thumbnailHeight;
        this.Width = thumbnailWidth;
    }
    public Bitmap GenerateThumbnail()
    {
        Thread thread = new Thread(new ThreadStart(GenerateThumbnailInteral));
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        thread.Join();
        return ThumbnailImage;
    }
    private void GenerateThumbnailInteral()
    {
        WebBrowser webBrowser = new WebBrowser();
        webBrowser.ScrollBarsEnabled = false;
        if (this.Method == ThumbnailMethod.Url)
            webBrowser.Navigate(this.Url);
        else webBrowser.DocumentText = this.Html;
        webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
        while (webBrowser.ReadyState != WebBrowserReadyState.Complete) Application.DoEvents();
        webBrowser.Dispose();
    }
    private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        WebBrowser webBrowser = (WebBrowser)sender;
        webBrowser.ClientSize = new Size(this.BrowserWidth, this.BrowserHeight);
        webBrowser.ScrollBarsEnabled = false;
        this.ThumbnailImage = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height);
        webBrowser.BringToFront();
        webBrowser.DrawToBitmap(ThumbnailImage, webBrowser.Bounds);
        this.ThumbnailImage = (Bitmap)ThumbnailImage.GetThumbnailImage(Width, Height, nullIntPtr.Zero);
    }
}

5 comments:

Venkat said...

Hi

Good Morning

Thanks for the code

At the same time if you put explaination for the above code it will be very useful

Thanks

Charith Shyam Gunasekara said...

Updated - 12/10/2010

Venkat said...

Thanks for the Updation

Anonymous said...

Thanks for the gr8 code
But the function
GenerateThumbnailInteral()
is taking infinite time and not returning anything.. Iam following the same your code ,is there anything else ???

Thanks

Charith Shyam Gunasekara said...

"Thanks for the gr8 code
But the function
GenerateThumbnailInteral()
is taking infinite time and not returning anything.. Iam following the same your code ,is there anything else ???"

This is because your web hosting provider does not allow STA threads.

iPhone Launch Screen Sizes

iPhone Portrait iOS 8 Retina HT 5.5 = 1242 X 2208 Retna HD 4.7 = 750 X 1134 iPhone Landscape iOS 8 Retina HD 5.5  2208 X 1242 iPho...