In addition to my automated unit, system and integration tests for ASP.NET projects, I like to give my customers something pretty that they can look at and visually see that the web site is behaving properly.  I use the Gallio test runner to produce a pretty HTML report, and WatiN (Web Application Testing In .NET) to test the UI and create screenshots.  I have a couple of issues with WatiN’s “CaptureWebPageToFile” method, though:     It blew up the first (and only) time I tried it, possibly because…     It scrolls down to capture the entire web page (I tried it on a very long page), and I usually don’t need that    Also, sometimes I don’t need a picture of the whole browser window - I just want a picture of the element that I'm testing (for example, proving that a button has the correct caption).  I wrote a WatiN screenshot saver helper class with these methods:     SaveBrowserWindowScreenshot(Watin.Core.IE ie)  /       SaveBrowserWindowScreenshot(Watin.Core.Element element)              saves a screenshot of the browser window             SaveBrowserWindowScreenshotWithHighlight(Watin.Core.Element element)              saves a screenshot of the browser window, with the specified element scrolled into view and highlighted             SaveElementScreenshot(Watin.Core.Element element)              saves a picture of only the specified element            The element highlighting improves on the built-in WatiN method (which just gives the element a yellow background, and makes the element pretty much unreadable when you have a light foreground color) by adding the ability to specify a HighlightCssClassName that points to a style in your site’s stylesheet.  This code is specifically for testing with Internet Explorer (‘cause that’s what I have to test with at work), but you’re welcome to take it and do with it what you want…  using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using SHDocVw;
using WatiN.Core;
using mshtml;
namespace BrianSchroer.TestHelpers
{
    public static class WatinScreenshotSaver
    {
        public static void SaveBrowserWindowScreenshotWithHighlight
            (Element element, string screenshotName)
        {
            HighlightElement(element, true);
            
            SaveBrowserWindowScreenshot(element, screenshotName);
            HighlightElement(element, false);
        }
        
        public static void SaveBrowserWindowScreenshotWithHighlight(Element element)
        {
            HighlightElement(element, true);
            SaveBrowserWindowScreenshot(element);
            HighlightElement(element, false);
        }
        public static void SaveBrowserWindowScreenshot(Element element, string screenshotName)
        {
            SaveScreenshot(GetIe(element), screenshotName, SaveBitmapForCallbackArgs);
        }
        public static void SaveBrowserWindowScreenshot(Element element)
        {
            SaveScreenshot(GetIe(element), null, SaveBitmapForCallbackArgs);
        }
        public static void SaveBrowserWindowScreenshot(IE ie, string screenshotName)
        {
            SaveScreenshot(ie, screenshotName, SaveBitmapForCallbackArgs);
        }
        public static void SaveBrowserWindowScreenshot(IE ie)
        {
            SaveScreenshot(ie, null, SaveBitmapForCallbackArgs);
        }
        public static void SaveElementScreenshot(Element element, string screenshotName)
        {
            // TODO: Figure out how to get browser window "chrome" size and not have to go to full screen:
            var iex = (InternetExplorerClass) GetIe(element).InternetExplorer;
            bool fullScreen = iex.FullScreen;
            if (!fullScreen) iex.FullScreen = true;
            ScrollIntoView(element);
            SaveScreenshot(GetIe(element), screenshotName, args => 
                SaveElementBitmapForCallbackArgs(element, args));
            iex.FullScreen = fullScreen;
        }
        public static void SaveElementScreenshot(Element element)
        {
            SaveElementScreenshot(element, null);
        }
        private static void SaveScreenshot(IE browser, string screenshotName, 
            Action<ScreenshotCallbackArgs> screenshotCallback)
        {
            string fileName = string.Format("{0:000}{1}{2}.jpg", 
                ++_screenshotCount,
                (string.IsNullOrEmpty(screenshotName)) ? "" : " ",
                screenshotName);
            string path = Path.Combine(ScreenshotDirectoryName, fileName);
            Console.WriteLine();
            // Gallio HTML-encodes the following display, but I have a utility program to
            // remove the "HTML===" and "===HTML" and un-encode the rest to show images in the Gallio report:
            Console.WriteLine("HTML===<div><b>{0}:</br></b><img src=\"{1}\" /></div>===HTML",
                screenshotName, new Uri(path).AbsoluteUri);
            MakeBrowserWindowTopmost(browser);
            try
            {
                var args = new ScreenshotCallbackArgs
                {
                    InternetExplorerClass = (InternetExplorerClass)browser.InternetExplorer,
                    ScreenshotPath = path
                };
                Thread.Sleep(100);
                screenshotCallback(args);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public static void HighlightElement(Element element, bool doHighlight)
        {
            if (!element.Exists) return;
            if (string.IsNullOrEmpty(HighlightCssClassName))
            {
                element.Highlight(doHighlight);
                return;
            }
            string jsRef = element.GetJavascriptElementReference();
            if (string.IsNullOrEmpty(jsRef)) return;
            
            var sb = new StringBuilder("try { ");
            sb.AppendFormat(" {0}.scrollIntoView(false);", jsRef);
            string format = (doHighlight)
                ? "{0}.className += ' {1}'"
                : "{0}.className = {0}.className.replace(' {1}', '')";
            sb.AppendFormat(" " + format + ";", jsRef, HighlightCssClassName);
            sb.Append("} catch(e) {}");
            string script = sb.ToString();
            GetIe(element).RunScript(script);
        }
        public static void ScrollIntoView(Element element)
        {
            string jsRef = element.GetJavascriptElementReference();
            if (string.IsNullOrEmpty(jsRef)) return;
            var sb = new StringBuilder("try { ");
            sb.AppendFormat(" {0}.scrollIntoView(false);", jsRef);
            sb.Append("} catch(e) {}");
            string script = sb.ToString();
            GetIe(element).RunScript(script);
        }
        public static void MakeBrowserWindowTopmost(IE ie)
        {
            ie.BringToFront();
            SetWindowPos(ie.hWnd, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
        }
        
        public static string HighlightCssClassName { get; set; }
        private static int _screenshotCount;
        private static string _screenshotDirectoryName;
        public static string ScreenshotDirectoryName
        {
            get
            {
                if (_screenshotDirectoryName == null)
                {
                    var asm = Assembly.GetAssembly(typeof(WatinScreenshotSaver));
                    var uri = new Uri(asm.CodeBase);
                    var fileInfo = new FileInfo(uri.LocalPath);
                    string directoryName = fileInfo.DirectoryName;
                    _screenshotDirectoryName = Path.Combine(
                        directoryName,
                        string.Format("Screenshots_{0:yyyyMMddHHmm}", DateTime.Now));
                    Console.WriteLine("Screenshot folder: {0}", _screenshotDirectoryName);
                    Directory.CreateDirectory(_screenshotDirectoryName);
                }
                return _screenshotDirectoryName;
            }
            set
            {
                _screenshotDirectoryName = value;
                _screenshotCount = 0;
            }
        }
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, 
            int X, int Y, int cx, int cy, uint uFlags);
        
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private const UInt32 SWP_NOSIZE = 0x0001;
        private const UInt32 SWP_NOMOVE = 0x0002;
        private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
        private static IE GetIe(Element element)
        {
            if (element == null) return null;
            var container = element.DomContainer;
            while (container as IE == null)
                container = container.DomContainer;
            return (IE)container;
        }
        private static void SaveBitmapForCallbackArgs(ScreenshotCallbackArgs args)
        {
            InternetExplorerClass iex = args.InternetExplorerClass;
            SaveBitmap(args.ScreenshotPath, iex.Left, iex.Top, iex.Width, iex.Height);
        }
        private static void SaveElementBitmapForCallbackArgs(Element element, ScreenshotCallbackArgs args)
        {
            InternetExplorerClass iex = args.InternetExplorerClass;
            Rectangle bounds = GetElementBounds(element);
            SaveBitmap(args.ScreenshotPath,
                iex.Left + bounds.Left,
                iex.Top + bounds.Top,
                bounds.Width,
                bounds.Height);
        }
        /// <summary>
        /// This method is used instead of element.NativeElement.GetElementBounds because that
        /// method has a bug (http://sourceforge.net/tracker/?func=detail&aid=2994660&group_id=167632&atid=843727).
        /// </summary>
        private static Rectangle GetElementBounds(Element element)
        {
            var ieElem = element.NativeElement as WatiN.Core.Native.InternetExplorer.IEElement;
            IHTMLElement elem = ieElem.AsHtmlElement;
            int left = elem.offsetLeft;
            int top = elem.offsetTop;
            for (IHTMLElement parent = elem.offsetParent; parent != null; parent = parent.offsetParent)
            {
                left += parent.offsetLeft;
                top += parent.offsetTop;
            }
            return new Rectangle(left, top, elem.offsetWidth, elem.offsetHeight);
        }
        private static void SaveBitmap(string path, int left, int top, int width, int height)
        {
            using (var bitmap = new Bitmap(width, height))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.CopyFromScreen(
                        new Point(left, top),
                        Point.Empty,
                        new Size(width, height)
                    );
                }
                bitmap.Save(path, ImageFormat.Jpeg);
            }
        }
        private class ScreenshotCallbackArgs
        {
            public InternetExplorerClass InternetExplorerClass { get; set; }
            public string ScreenshotPath { get; set; }
        }
    }
}