Getting and using the pixels of your Visual in WPF

Have you ever wanted to get the Bitmap Data (pixels) of a Visual (Canvas, Grid, etc) in WPF or Silverlight from memory and without having to add it to the root visual. After doing a lot of trial and error here is a quick piece of code and a sample project that can help you accomplish exactly that.

There are plenty of cool ways to extend this project including some cool transition and other code based animations. Just let your imagination go wild!

First, create a new WPF Application in Visual Studio or [download a sample project]. In your Window1.xaml give the Grid an x:Name value. You’ll use this to add your elements later on.

<br  /><grid x:Name="VisualRoot"></grid><br />

Now lets add a new Page to our project. We’re going to alter the Page to extend a Canvas rather than a Page for no other reason then when I’m creating animations I prefer to work with a Canvas. We are going to put some visuals onto our canvas. In the sample it’s a blue square with a red circle next to it and a little bit of text below it. It could be any UIElement including another Canvas/Grid, Images, Text anything.

In our Window1.xaml code behind we’re going to create an instance of our Canvas1 and call the methods to have WPF Measure, Arrange, and UpdateLayout in memory. We need to call these methods in order for WPF to figure out where all the pixels are positioned in our visual to return accurate Bitmap Data. These methods are called automatically when you add a Visual to WPF using the Visual.Children.Add() method.

The CreateBitmapSourceFromVisual function was taken from a project on CodePlex, unfortunately I can’t recall the link. I’ll add it here if I run across it again. That function takes a Visual and returns a BitmapSource object with the pixel data. It automatically updates the dots per inch (DPI) to 96, which is required for WPF to render properly).

Here is the code behind from our root Window.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace BitmapFromVisualSample
{
    /// 
    /// Interaction logic for Window1.xaml
    /// 
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            Canvas1 myVisualElement = new Canvas1();
            myVisualElement.Measure(new System.Windows.Size(0, 0));
            myVisualElement.Arrange(new Rect(new System.Windows.Size(0, 0)));
            myVisualElement.UpdateLayout();
            // Note the myVisualElement is never added

            // Use myVisualElement to get our Bitmap Source Data.
            BitmapSource myVisualElementBitmapData = CreateBitmapSourceFromVisual(300, 500, myVisualElement, false);
            // Create an Image for your Bitmap Source
            Image myImage = new Image();
            // use our Bitmap Data 
            myImage.Source = myVisualElementBitmapData;

            // Add our Image to the Grid
            VisualRoot.Children.Add(myImage);
        }
        public BitmapSource CreateBitmapSourceFromVisual(Double width, Double height, Visual visualToRender, Boolean undoTransformation)
        {
            if (visualToRender == null)
            {
                return null;
            }
            RenderTargetBitmap bmp = new RenderTargetBitmap(
                                            (Int32)Math.Ceiling(width),
                                            (Int32)Math.Ceiling(height),
                                            96,
                                            96,
                                            PixelFormats.Pbgra32);
            if (undoTransformation)
            {
                DrawingVisual dv = new DrawingVisual();
                using (DrawingContext dc = dv.RenderOpen())
                {
                    VisualBrush vb = new VisualBrush(visualToRender);
                    System.Windows.Point myPoint = new System.Windows.Point();
                    dc.DrawRectangle(vb, null, new Rect(myPoint, new System.Windows.Size(width, height)));
                }
                bmp.Render(dv);
            }
            else
            {
                bmp.Render(visualToRender);
            }
            return bmp;
           
        }
    }
}

You can run the sample project and you will see the visual we created in Canvas1. We used the BitmapSource object of the Canvas1 as the Source of our Image. Note: we never added Canvas1 to the root visual, we simply created it in the code behind and retrieved the pixel data from that visual to use as on our Image. I leave it to you to play with this technology to create cool WPF/Silverlight animations.

[Download Source Code] as Visual Studio Project.

Leave a Reply