Click here to Skip to main content
Click here to Skip to main content

Tagged as

How to Render Bitmap or to Print a Visual in WPF

, 19 Aug 2010
Rate this:
Please Sign up or sign in to vote.
The article shows how you could use RenderTargetBitmap to render a visual into BitmapSource and convert the same into actual image.

Working with WPF would never become so easy if there was no inbuilt capability to take snaps or print a visual object. Microsoft made a very constructive class hierarchy to add functionality in each of the classes and hence built the solid foundation of WPF presentation model. Every element that we place in WPF is inherited from a Visual. BCL added inbuilt functionality for a visual to render itself as Bitmapsource or to print the same Visual directly to the printer. In this post, I will discuss how easily you could render a Visual as BitmapSource and later use it as source of an Image control.

Using Sample Application

RenderTargetBitmap is a class that is basically used to render a Visual to a bitmap object. Let's demonstrate this using an InkCanvas. You might already know, there is a special canvas element called InkCanvas which lets the user draw an image on the screen. So before we proceed with the code, let's discuss the sample application a bit.

In the above snap, you can see that I have placed one InkCanvas which allows you to write dynamically on the screen. The buttons will Render the bitmap image from the Canvas and add the item on the ListBox on the Right. You can see after I click on Render as Bitmap button, it actually places the same visual as BitmapSource on the Right hand side ListBox.

Similar to what we worked on, I changed the content a bit and took another shot, and it exactly does the same.

Finally, when I click on Render the Grid as Bitmap, it actually renders the whole Grid including the listbox and all as Grid. There are few buttons more, like Save Selection as JPEG / Print as Visual, each of them has its own functionality.

You can download the sample here.

Using the Code

So, as you see the basic functionality, let me discuss the code.

<InkCanvas EditingMode="Ink" Grid.Row="1" x:Name="inkCanvas" Background="AliceBlue" />
        <ListBox x:Name="lstImages" Grid.Row="1" Grid.Column="1" 
		ItemsSource="{Binding ImageCollection}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Image Source="{Binding}" Stretch="UniformToFill" 
			MaxWidth="150" MaxHeight="150" Margin="10" />
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" Width="{Binding 
                        RelativeSource={RelativeSource Mode=FindAncestor, 
			AncestorType={x:Type ScrollContentPresenter}}, 
			Path=ActualWidth}" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>
        <StackPanel Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal">
            <Button x:Name="btnRenderBitmap" Content="Render as BitMap" 
		Click="btnRenderBitmap_Click" />
            <Button x:Name="btnRenderwhole" Content="Render the Grid as Bitmap" 
		Click="btnRenderwhole_Click" />
            <Button x:Name="btnRenderJPEG" Content="Save Selecteditem as Jpeg" 
		Click="btnRenderJPEG_Click"  />
            <Button x:Name="btnPrintVisual" Content="Print the Visual" 
		Click="btnPrintVisual_Click"  />
        </StackPanel>

So here I have placed one InkCanvas element and a ListBox with few buttons. The XAML is quite straightforward, and when buttons are clicked, the listbox will update itself.

private ObservableCollection<BitmapSource> imagecollection;
public ObservableCollection<BitmapSource> ImageCollection
{
    get
    {
        this.imagecollection = this.imagecollection ?? 
		new ObservableCollection<BitmapSource>();
        return this.imagecollection;
    }
}

public RenderTargetBitmap RenderVisaulToBitmap(Visual vsual, int width, int height)
{
    RenderTargetBitmap rtb = new RenderTargetBitmap
		(width, height, 96, 96, PixelFormats.Default);
    rtb.Render(vsual);

    BitmapSource bsource = rtb;
    this.ImageCollection.Add(bsource);
    return rtb;
}

I have used a model to use RenderTargetBitmap. Basically, the model will actually separate the logic from the presentation. I have created an ObservableCollection of BitmapSource objects. Now when Button1 is clicked, I use RenderTargetBitmap to render the Visual.

RenderTargetBitmap takes width, height, dpiX and dpiY as arguments and renders the BitmapSource object from the Visual. rtb.Render(visual) gets the BitmapSource object, which I have added to the ImageCollection. As it is directly bound to the ListBox, the ListBox updates the Image instantly.

To Encode the BitmapSource to Actual Image

Rendering an image from a BitmapSource object is actually not a big deal. Let us look at the code below:

public MemoryStream GenerateImage
	(Visual vsual, int widhth, int height, ImageFormat format)
{
    BitmapEncoder encoder = null;

    switch (format)
    {
        case ImageFormat.JPG :
            encoder = new JpegBitmapEncoder();
            break;
        case ImageFormat.PNG:
            encoder = new PngBitmapEncoder();
            break;
        case ImageFormat.BMP:
            encoder = new BmpBitmapEncoder();
            break;
        case ImageFormat.GIF:
            encoder = new GifBitmapEncoder();
            break;
        case ImageFormat.TIF:
            encoder = new TiffBitmapEncoder();
            break;
    }

    if (encoder == null) return null;

    RenderTargetBitmap rtb = this.RenderVisaulToBitmap(vsual, widhth, height);
    MemoryStream file = new MemoryStream();
    encoder.Frames.Add(BitmapFrame.Create(rtb));
    encoder.Save(file);

    return file;
}

Here, I have used an enumerable which decides the format of the image. In the method, when I pass JPEG, it will actually create an object of JpegBitmapEncoder which lets me to encode the BitmapSource into compressed JPEG format. encoder.Frames allows you to add a BitmapFrame which would later be rendered as Image. You can use JpegBitmapDecoder to decode JPEG image. Once I call encoder.Save, it saves the image into Stream.

private void btnRenderJPEG_Click(object sender, RoutedEventArgs e)
{
    MemoryStream memstream = this.DataModel.GenerateImage
    (inkCanvas, (int)inkCanvas.ActualWidth, (int)inkCanvas.ActualHeight, ImageFormat.JPG);

    if (memstream != null)
    {
        SaveFileDialog fdlg = new SaveFileDialog
        {
             DefaultExt="jpg",
             Title="Choose  filename and location",
             Filter="*Jpeg files|.jpg|Bmp Files|*.bmp|PNG Files|
			*.png|Tiff Files|*.tif|Gif Files|*.gif"
        };

        bool? result = fdlg.ShowDialog();

        if (result.HasValue && result.Value)
        {
            using (FileStream fstream = File.OpenWrite(fdlg.FileName))
            {
                memstream.WriteTo(fstream);
                fstream.Flush();
                fstream.Close();
            }
        }
    }
}

From the front end, I call the method, and store the stream into a file location.

Printing a Visual

Printing a Visual WPF is somewhat the easiest of the whole lot if you want to print the inkCanvas directly to the printer. In that case, you don't need to create a PrintDocument. I will discuss how to print FlowDocument later. To print a visual, you need to use PrintDialog.

PrintDialog dlg = new PrintDialog();
dlg.PrintVisual(this.inkCanvas, "The picture is drawn dynamically");

Thus PrintDialog has a method that allows you to pass a description and a visual, and eventually the command will print the visual from printer.

Try the sample application here.

I hope this has made it clear on how to work with Visual. If you want to comment, feel free to do so.
Thanks for reading.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Abhishek Sur
Architect
India India
Did you like his post?
 
Oh, lets go a bit further to know him better.
Visit his Website : www.abhisheksur.com to know more about Abhishek.
 
Abhishek also authored a book on .NET 4.5 Features and recommends you to read it, you will learn a lot from it.
http://bit.ly/EXPERTCookBook
 
Basically he is from India, who loves to explore the .NET world. He loves to code and in his leisure you always find him talking about technical stuffs.
 
Presently he is working in WPF, a new foundation to UI development, but mostly he likes to work on architecture and business classes. ASP.NET is one of his strength as well.
Have any problem? Write to him in his Forum.
 
You can also mail him directly to abhi2434@yahoo.com
 
Want a Coder like him for your project?
Drop him a mail to contact@abhisheksur.com
 
Visit His Blog

Dotnet Tricks and Tips



Dont forget to vote or share your comments about his Writing
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralMy vote of 5 PingroupSaraf Talukder12-Aug-11 9:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 19 Aug 2010
Article Copyright 2010 by Abhishek Sur
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid