Rendering WPF Visuals to Bitmap

Sometimes we need to convert a WPF Window or Control (or any other type of Visual) to a plain old GDI+ Bitmap. In WinForms, creating a Bitmap of a Window or Control is quite complex. In WPF it's a piece of cake, at least if you're comfortable with the citizens of the System.Windows.Media.Imaging namespace, like RenderTargetBitmap, and the range of BitmapEncoders. I created a little helper class with some extension methods to Visual to facilitate the process. The class comes with a PngBitmap method that returns a Bitmap (PNG format), and a BitmapSource method for data binding, e.g. to a WPF Image control.

This is a screenshot of a small sample. The textbox and ellipse in the upper part of the window are original WPF Controls. The lower part of the window is a bitmap of the upper part, displayed in an Image control:



The Window has the following XAML content:

    <StackPanel>
        <TextBlock 
            Margin="10"
            Text="Dock of the Bay" />
        <Ellipse 
            Margin="10"
            Height="50" Width="50"
            Fill="DeepSkyBlue" Stroke="MidnightBlue"
            HorizontalAlignment="Left" />
        <Image
            x:Name="ScreenShotImage" />
    </StackPanel>


The screenshot is taken, and displayed in the Image control with the following one-liner:

   this.ScreenShotImage.Source = this.BitmapSource();

Here's the whole extension class:

//-----------------------------------------------------------------------
// <copyright file="VisualExtensions.cs" company="DockOfTheBay">
//     http://www.dotbay.be
// </copyright>
// <summary>Defines the VisualExtensions class.</summary>
//-----------------------------------------------------------------------
 
namespace DockOfTheBay
{
    using System;
    using System.Drawing;           // Need reference to System.Drawing.dll
    using System.Windows;
    using System.Windows.Interop;   // Need reference to PresentationCore.dll
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
 
    /// <summary>
    /// Extension Methods for the System.Windows.Media.Visual Class
    /// </summary>
    public static class VisualExtensions
    {
        /// <summary>
        /// Returns the contents of a WPF Visual as a Bitmap in PNG format.
        /// </summary>
        /// <param name="visual">A WPF Visual.</param>
        /// <returns>A GDI+ System.Drawing.Bitmap.</returns>
        public static Bitmap PngBitmap(this Visual visual)
        {
            // Get height and width
            int height = (int)(double)visual.GetValue(FrameworkElement.ActualWidthProperty);
            int width = (int)(double)visual.GetValue(FrameworkElement.ActualHeightProperty);
 
            // Render
            RenderTargetBitmap rtb =
                new RenderTargetBitmap(
                    height,
                    width,
                    96,
                    96,
                    PixelFormats.Default);
            rtb.Render(visual);
 
            // Encode
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(rtb));
            System.IO.MemoryStream stream = new System.IO.MemoryStream();
            encoder.Save(stream);
 
            // Create Bitmap
            Bitmap bmp = new Bitmap(stream);
            stream.Close();
 
            return bmp;
        }
 
        /// <summary>
        /// Returns the contents of a WPF Visual as a BitmapSource, e.g.
        /// for binding to an Image control.
        /// </summary>
        /// <param name="visual">A WPF Visual.</param>
        /// <returns>A set of pixels.</returns>
        public static BitmapSource BitmapSource(this Visual visual)
        {
            Bitmap bmp = visual.PngBitmap();
            IntPtr hBitmap = bmp.GetHbitmap();
            BitmapSizeOptions sizeOptions = BitmapSizeOptions.FromEmptyOptions();
            return Imaging.CreateBitmapSourceFromHBitmap(
                                hBitmap,
                                IntPtr.Zero,
                                Int32Rect.Empty,
                                sizeOptions);
        }
    }
}


By the way, if you prefer to screen capture to XPS instead of to a Bitmap, then I suggest this article.

No comments:

Post a Comment