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:
Cheers Thomas