Blake O'Hare .com

How to convert a UI Control in WPF/Silverlight/WinForms into a Bitmap

Sometimes you just need to convert a FrameworkElement/Control into a bitmap even though the control doesn't necessarily exist in the UI Tree. Usually people find themselves in this situation because the 2D Graphics Drawing library for WPF/Silverlight is completely non-existent and you just need a cheap way to draw a circle or text.

WPF

If the element was created programmatically and doesn't exist in the UI tree, you need to force it to update its layout using Measure/Arrange.

// using System, System.Windows, System.Windows.Media, System.Windows.Media.Imaging
public BitmapSource CreateBitmap(FrameworkElement element, bool isInUiTree)
{
    if (!isInUiTree)
    {
        element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        element.Arrange(new Rect(new Point(0, 0), frameworkElement.DesiredSize));
    }
    
    int width = (int)Math.Ceiling(element.ActualWidth);
    int height = (int)Math.Ceiling(element.ActualHeight);

    width = width == 0 ? 1 : width;
    height = height == 0 ? 1 : height;

    RenderTargetBitmap rtbmp = new RenderTargetBitmap(
        width, height, 96, 96, PixelFormats.Default);
    rtbmp.Render(element);
    return rtbmp;
}

The Render method of RenderTargetBitmap can take in any Visual, not just a FrameworkElement. Measure and Arrange exist on the UIElement class but the ActualWidth and ActualHeight properties only exist on FrameworkElement, so you need to know what the bounds are if it's a UIElement. If it's just a visual, then you there is no Measure or Arrange. It must already be in the UI Tree in order for it to work.

Silverlight

Silverlight is a bit easier. All you have to do is simply pass the UIElement into one of the constructors of WriteableBitmap. No Measure/Arrange silliness.


// using System, System.Windows, System.Windows.Media.Imaging
public BitmapSource CreateBitmap(FrameworkElement element)
{
    int width = (int)Math.Ceiling(element.ActualWidth);
    int height = (int)Math.Ceiling(element.ActualHeight);

    width = width == 0 ? 1 : width;
    height = height == 0 ? 1 : height;

    return new WriteableBitmap(element, null);
}


WinForms

Why are you still using WinForms? Anyways...

// using System, System.Drawing, System.Windows.Forms,
public Bitmap CreateBitmap(Control winformsControl)
{
    int width = (int)Math.Ceiling(winformsControl.Width);
    int height = (int)Math.Ceiling(winformsControl.Height);

    width = width == 0 ? 1 : width;
    height = height == 0 ? 1 : height;
    
    Bitmap bmp = new Bitmap(width, height);
    winformsControls.DrawToBitmap(bmp, new Rectangle(0, 0, width, height));

    return bmp;
}