Often it is a good thing to visualize a link to something with a thumb of the page you linked to. Imagine you have a link-module where you sample interesting links to, let’s say … pizza services. You want to add a screenshot of their homepage to help your visitors to identify the pizza service they are looking for. But adding all these thumbs manually is something you don’t wish your hardest enemy has to do. So, how can we accomplish this automatically ?
The WebBrowser Control
Most of the magic is done by the WebBrowser Control. We can instantiate a WebBrowser-Control, let it navigate to the Url we entered before.
WebBrowser ieBrowser = new WebBrowser();
ieBrowser.ScrollBarsEnabled = false;
ieBrowser.ScriptErrorsSuppressed = true;
ieBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(IEBrowser_DocumentCompleted);
ieBrowser.Navigate(Url);
When the DocumentCompleted - event is fired, we are able to take a snapshot of the content. We have to deal a little bit with scrollbars but that is no magic at all.
void IEBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser browser = (WebBrowser)sender;
browser.Width = doc.Body.ScrollRectangle.Width;
browser.Height = doc.Body.ScrollRectangle.Height;
Bitmap bitmap = new Bitmap(browser.Width, browser.Height);
browser.DrawToBitmap(bitmap, new Rectangle(0, 0, browser.Width, browser.Height));
browser.Dispose();
bitmap.Save(File, ImageFormat.Jpeg);
}
Ups we need this in a static thread
If you integrate this in your DNN-Module and try to use it you get an error. You have to run the WebBrowser Control in a static thread. So we have to do a little bit more. Lets add a new class to our module:
using System.Windows.Forms;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
public class IEBrowser : System.Windows.Forms.ApplicationContext
{
string File;
AutoResetEvent ResultEvent;
public IEBrowser(string url, string file, AutoResetEvent resultEvent)
{
ResultEvent = resultEvent;
Thread thrd = new Thread(new ThreadStart( delegate { Init(url, file); System.Windows.Forms.Application.Run(this); }));
// set thread to STA state before starting
thrd.SetApartmentState(ApartmentState.STA);
thrd.Start();
}
private void Init(string Url, string file)
{
// create a WebBrowser control
WebBrowser ieBrowser = new WebBrowser();
ieBrowser.ScrollBarsEnabled = false;
ieBrowser.ScriptErrorsSuppressed = true;
// set WebBrowser event handle
ieBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(IEBrowser_DocumentCompleted);
ieBrowser.Navigate(Url); File = file;
}
// DocumentCompleted event handle
void IEBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
WebBrowser browser = (WebBrowser)sender;
browser.Width = doc.Body.ScrollRectangle.Width;
browser.Height = doc.Body.ScrollRectangle.Height;
Bitmap bitmap = new Bitmap(browser.Width, browser.Height);
browser.DrawToBitmap(bitmap, new Rectangle(0, 0, browser.Width, browser.Height));
browser.Dispose();
bitmap.Save(File, ImageFormat.Jpeg);
}
catch (System.Exception) { }
finally
{
ResultEvent.Set();
}
}
}
Now you can call the following code to save the snapshot:
AutoResetEvent resultEvent = new AutoResetEvent(false);
IEBrowser browser = new IEBrowser("http://www.bitboxx.net", @"C:\InetPub\wwwroot\dnn\Portals\25\images/thumbs\22.jpg", resultEvent);
EventWaitHandle.WaitAll(new AutoResetEvent[] { resultEvent });
Thats all. Only some bits of code but impressive when you see it in action.
Just another tip. If you want to save a snapshot from a HTML-snippet and not from a webpage, you can inject this HTML into the WebBrowser control. The only thing you have to change is navigating to “about:blank” and in the DocumentCompleted-event add a little bit of code:
HtmlDocument doc = browser.Document;
doc.OpenNew(true);
doc.Write(Html);
If you read my blog post about flexible module design with templates, you can use this mimic to visualize the entered HTML in the template textbox. The only thing you must have in mind is, that you have to add references to all CSS files to your generated HTML. And don’t forget to add the opening and closing and tags
Example:
private void GenThumb(ProductTemplateInfo Template, string thumb)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("<html><head>");
sb.AppendLine("<link href=\"file:///" + Server.MapPath("~/Portals/_default/default.css") + "\" rel=\"stylesheet\" type=\"text/css\" />");
sb.AppendLine("<link href=\"file:///" + Server.MapPath("~/DesktopModules/BBStore/module.css") + "\" rel=\"stylesheet\" type=\"text/css\" />");
sb.AppendLine("<link href=\"file:///" + Server.MapPath(PortalSettings.ActiveTab.SkinPath + "skin.css") + "\" rel=\"stylesheet\" type=\"text/css\" />");
sb.AppendLine("<link href=\"file:///" + Server.MapPath(PortalSettings.HomeDirectory + "portal.css") + "\" rel=\"stylesheet\" type=\"text/css\" />");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.Append(Template.Template);
sb.AppendLine("</body></html>");
sb.Replace("[TITLE]", "Product Title");
sb.Replace("[PRODUCTDESCRIPTION]", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum .");
sb.Replace("[PRODUCTOPTIONS]", "<span class=\"Normal\">ProductOption</span> <select class=\"NormalTextBox\" name=\"Select1\"><option>Option1</option></select>");
sb.Replace("[MANDATORYERROR]", "Error Message");
sb.Replace("[PRICE]", "123.44");
sb.Replace("[CURRENCY]", PortalSettings.Currency);
sb.Replace("[ADDCARTIMAGE]", "<img src=\"file:///" + Server.MapPath("~/images/cart.gif") + "\" />");
sb.Replace("[ADDCARTLINK]", "Add to cart");
sb.Replace("[TAX]", "includes tax (19%)");
sb.Replace("[AMOUNT]", "<input name=\"Text1\" type=\"text\" size=\"3\" value=\"1\"/>");
AutoResetEvent resultEvent = new AutoResetEvent(false);
IEBrowser browser = new IEBrowser(false, sb.ToString(), thumb, resultEvent);
EventWaitHandle.WaitAll(new AutoResetEvent[] { resultEvent });
}