How to print dynamically created Images in Silverlight 4 Beta

Silverlight 4 supports printing scenarios. It’s quite easy. Just create a PrintDocument instance, handle the PrintPage-Event and call the Print-Method. In the PrintPage-Event set the PageVisual-Property of the PrintPageEventArgs to a UIElement of your choice. If there are more pages, set the HasMorePages-Property of the PrintPageEventArgs to true and the PrintPage-Eventhandler would be called again for the next page.

Below a simple example using a lambda expression. When the Print-Method is called a PrintDialog is displayed to the User, where he can select the printer of his choice. When the PrintDialog was accepted, the PrintPage-Event gets fired and the lambda expression below get’s called. The PageVisual-Property is set to a TextBlock. So that TextBlock with the text “Thoams says…” is printed out.

var pd = new PrintDocument();
pd.PrintPage += (s, e) =>
  {
    e.PageVisual = new TextBlock {Text="Thomas says Hello"};
  };
pd.Print();

Ok, so far so good. As I was working on an example for my upcoming Silverlight 4 book I needed to create an Image-Element on the fly and print this out. And then I noticed that the Image doesn’t appear on the output.

While searching for a solution I found somebody having the same problem in this thread in Microsoft’s Silverlight forums:

http://forums.silverlight.net/forums/t/145680.aspx

So, it seemed it was not my cause, it was a Beta-cause. So let’s look at a workaround. But first look at the bug.

I made a smaller example to reproduce it. View the following code. What do you think is printed on the page?

void PrintButton_Click(object sender, RoutedEventArgs e)
{
  var streamResourceInfo =
    Application.GetResourceStream(
      new Uri("thomas.png", UriKind.Relative));

  var bitmapImage = new BitmapImage();
  bitmapImage.SetSource(streamResourceInfo.Stream);

  var image = new Image
  {
    Width = bitmapImage.PixelWidth,
    Height = bitmapImage.PixelHeight,
    Source = bitmapImage
  };

  var pd = new PrintDocument();
  pd.PrintPage += (s, args) =>
    {
      args.PageVisual = image;
    };
  pd.Print();
}

Right, an Image should be printed on the page. But it isn’t. The page is empty. Well, the next thing I tried was to call Measure, Arrange and UpdateLayout on the Image to force a layout-pass. But anyway, it didn’t work, the printed page is always empty.

When the Image isn’t created on the fly, it works. Define the Image in XAML like this

<Image Source="thomas.png" x:Name="image"/>

and a Print-Method in the Codebehind-File would work like that:

void PrintButton_Click(object sender, RoutedEventArgs e)
{
  var pd = new PrintDocument();
  pd.PrintPage += (s, args) =>
    {
      args.PageVisual = image;
    };
  pd.Print();
}

But we want to print an Image on the fly. So how to do that? One way I found out was to create an ImageBrush and set its ImageSource-Property to the BitmapImage. Use the ImageBrush for a Rectangle’s Fill-Property and print out that Rectangle. So here is some code to dynamically print an image by using an ImageBrush in combination with a Rectangle:

void PrintButton_Click(object sender, RoutedEventArgs e)
{
  var streamResourceInfo =
    Application.GetResourceStream(
      new Uri("thomas.png", UriKind.Relative));

  var bitmapImage = new BitmapImage();
  bitmapImage.SetSource(streamResourceInfo.Stream);

  var imageBrush = new ImageBrush();
  imageBrush.ImageSource = bitmapImage;

  var rectangle = new Rectangle
  {
    Width = bitmapImage.PixelWidth,
    Height = bitmapImage.PixelHeight,
    Fill = imageBrush
  };

  var pd = new PrintDocument();
  pd.PrintPage += (s, args) =>
    {
      args.PageVisual = rectangle;
    };
  pd.Print();
}

And voilà, the output looks like this when printed to my PDFCreator-Printer:

image

[Download the Source]

Cheers Thomas

Share this post

Comments (5)

  • Rodrigo Díaz Concha Reply

    Hey Thomas, nice workaround!

    I still believe this is a Beta issue, hopefully it will be fixed in the RTW version.

    For example, Images are not working neither on a DataTemplate for an ItemsControl when you set that ItemsControl as the PageVisual.

    Thanks.

    November 25, 2009 at 11:55 pm
  • hubethom Reply

    Hi Rodrigo,

    Yes, i also think that it’s a beta issue. I think they will fix it for the rtw.

    Thomas

    November 26, 2009 at 7:49 am
  • ben roberts Reply

    what a load of crap silverlight is .
    how can microsoft make this all so hard.
    this is not even a beta issue!!!

    the printing api is pitifull, the documentation appauling.

    i need to write code for a business application which requires me to print out
    a number of images tables textboxes dynamically and there no way i can do
    it without creating the objects before time. and guessing the size of the printing area…

    what a load of ****

    July 6, 2010 at 4:44 pm
  • ps2goat Reply

    Yeah, it looks like it has to inherit from UIElement (or FrameworkElement), i.e., a GUI control.

    The printout info gets rendered prior to printing, so everything comes out as an image. This is such a bummer.

    December 23, 2010 at 2:36 am
  • Steve Smith Reply

    Many thanks! Saved me a ton of frustration and time.

    August 19, 2013 at 5:09 am

Leave a Reply

Your email address will not be published. Required fields are marked *

*