SpriteHand
Module Border
  "Printing" in Silverlight 3 with WriteableBitmap
Module Border
Location: BlogsAndy's Blog    
Posted by: host 7/10/2009 8:30 AM


One of the high-profile missing features in Silverlight has been Printing support. If you have ever tried to print a web page containing Silverlight content, what you saw on the printed page may be skewed or even missing altogether!  So, what if you wanted to print a portion of your Silverlight screen, or take a “snapshot” image of the Silverlight UI to include in a report or other printable format?

Silverlight 3 can accomplish these scenarios using the WriteableBitmap API. WritableBitmap includes a Render method which can snag all of the pixels of a given UI Element and place them into a buffer for manipulation.

In this demo, I’ll show how to take a “snapshot” of a Silverlight UI screen, upload the image to a web server, and include it in a Report Viewer (RDLC) report. You could easily modify these steps to save the snapshot to an image file on the server, or otherwise manipulate the pixels.

VIEW THE DEMO        

DOWNLOAD THE SOURCE

The steps used in the demo are as follows:

1.       Render the Silverlight UI to a WriteableBitmap, passing in the UI Element and an arbitrary Transform. In the code below, the content of a Canvas named cnvSource is rendered to the WriteableBitmap. We pass in an empty TranslateTransform simply because one is required by the constructor:

WriteableBitmap bitmap = new WriteableBitmap(cnvSource, new TranslateTransform());


2. Convert the WriteableBitmap pixels to a PNG using Joe Stegman's PNG encoder.

EditableImage imageData = new EditableImage(bitmap.PixelWidth, bitmap.PixelHeight);

 

 

for (int y = 0; y < bitmap.PixelHeight; ++y)

{

    for (int x = 0; x < bitmap.PixelWidth; ++x)

    {

 

        int pixel = bitmap.Pixels[bitmap.PixelWidth * y + x];

 

        imageData.SetPixel(x, y,

                    (byte)((pixel >> 16) & 0xFF),

                    (byte)((pixel >> 8) & 0xFF),

                    (byte)(pixel & 0xFF),

                    (byte)((pixel >> 24) & 0xFF)

                    );

 

    }

}

 

Stream pngStream = imageData.GetStream();

 


NOTE that this PNG encoder does NOT include compression! This would be a good optimization to add, but also note that the GZipStream class is not present in Silverlight, so you would need to use an outside compression library such as SharpZipLib.

3. At this point, we have the PNG bytes in a stream, and you could take several approaches to get these bytes up to the server – such as using an Http Handler (ASHX). In this demo, we’ll place the bytes into a hidden field on the ASPX page and post the page back to the server  for inclusion in a report. To do this, we’ll translate the PNG bytes into a string using Base64 encoding:

byte[] binaryData = new Byte[pngStream.Length];

long bytesRead = pngStream.Read(binaryData, 0, (int)pngStream.Length);

 

string base64String =

        System.Convert.ToBase64String(binaryData,

                                      0,

                                      binaryData.Length);

 

// save the encoded PNG bytes to the page

HtmlDocument document = HtmlPage.Document;

HtmlElement txtPNGBytes = document.GetElementById("txtPNGBytes");

txtPNGBytes.SetProperty("value", base64String);

 

// this calls a js function "postBackPrint" which will cause a postback

HtmlPage.Window.CreateInstance("postBackPrint", new string[] { });



4. Now that we have our bytes up on the server, we can decode them and feed them to a ReportViewer (RDLC) report. This will give us a nicely printed format and the ability to export to PDF:

string bytes64 = Request["txtPNGBytes"];

byte[] imageBytes = System.Convert.FromBase64String(bytes64);

 

 

DSReportPrintImage ds = new DSReportPrintImage();

DataRow drImage = ds.Tables[0].NewRow();

drImage["ImageBytes"] = imageBytes;

ds.Tables[0].Rows.Add(drImage);

 

ReportViewer1.LocalReport.ReportPath = "ReportPrintSilverlight.rdlc";

 

ReportDataSource src = new ReportDataSource("DSReportPrintImage_ImageData", ds.Tables[0]);

ReportViewer1.LocalReport.DataSources.Add(src);

ReportViewer1.LocalReport.Refresh();

That’s it! I really think this use of WriteableBitmap as a snapshot/print function will be useful in some of my projects that need to capture the current view of the Silverlight application.

 

Permalink |  Trackback

Comments (30)   Add Comment
Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/10/2009 2:18 PM
I want to save my image to hard drive using SaveFileDialog.
Can you help me ?

andy.beaulieu.com > Home - "Printing" in Silverlight 3 with WriteableBitmap    By TrackBack on 7/11/2009 7:59 AM
Thank you for submitting this cool story - Trackback from NewsPeeps
# NewsPeeps

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/11/2009 9:46 AM
Can you help me i want to save the image to a sql database with a webservice but i don't now how to do that.

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/11/2009 11:08 AM
If you want to save the image using SaveFileDialog please visit http://silverlight.net/forums/p/108713/247204.aspx

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/12/2009 1:26 PM
Thank you. I tried the demo, it work but seemed to be very slow for me. maybe just the network?

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/12/2009 2:23 PM
Excellent post !!!

I have combined your code with some bit and pieces of another one and created a lib to export a canvas to PNG:

http://geekswithblogs.net/braulio/archive/2009/07/12/export-canvas-to-png-and-save-it-in-your-local.aspx

Thanks a lot
Braulio

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/12/2009 7:40 PM
Yeah, sorry the demo is slow. Note that the image data is NOT compressed, and in a real world scenario you should add compression using a library like SharpZipLib (see notes in post). In the demo it is sending about 700kb up to the server and it is slow but the compression would take care of that.

-Andy

Silverlight 3 WriteableBitmap to Jpg    By TrackBack on 7/17/2009 10:01 PM
Silverlight 3 WriteableBitmap to Jpg
# Roberto Lopez's Coding Journal

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/18/2009 5:25 AM
Add following method to PngEncoder class and you'll be able to save writable bitmap

public static Stream Encode(WriteableBitmap wb)
{
int w = wb.PixelWidth;
int h = wb.PixelHeight;
int adr;

int rowLength = w * 4 + 1;

byte[] buffer = new byte[rowLength * h];

for (int y = 0; y < h; y++)
{
buffer[y * rowLength] = 0; // filter byte

for (int x = 0; x < w; x++)
{
adr = y * rowLength + x * 4 + 3;

int pixel = wb.Pixels[x + y * w];

buffer[adr--] = (byte)(pixel & 0xff); pixel >>= 8;
buffer[adr--] = (byte)(pixel & 0xff); pixel >>= 8;
buffer[adr--] = (byte)(pixel & 0xff); pixel >>= 8;
buffer[adr] = (byte)(pixel & 0xff);
}
}

return PngEncoder.Encode(buffer, w, h);
}

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/27/2009 9:07 PM
My canvas that I want to print contains a scrollviewer. Is it possible to print items that are not visible and must be scrolled to?

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/27/2009 9:44 PM
For the ScrollViewer, I suppose you would have to place some kind of a Container inside the ScrollViewer, and then use the method above to save or print that Container to the WriteableBitmap.

-Andy

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/27/2009 10:01 PM
Hi Andy,
Thanks for replying so quickly. The problem is I have a header-detail scenario with the canvas. The header is a grid and the detail is a scrollviewer containing a grid. So if I printed the scrollviewer contents it would have no header.

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/28/2009 12:07 PM
Hmmm... yeah that is a good one. I can't think of a clean way. But as a hack, you could maybe repeat the header in another Canvas, and then Remove (Children.Remove()) the Grid from the ScrollViewer and add it to the new Canvas before the print. Then put it back in the ScrollViewer after the Print.

Of course you still would need to figure out Paging if you need it :( That is a big flaw in this "screenshot" method.

-Andy

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/29/2009 8:55 AM
When trying the demo, I note that the PNG is more whiter than the original

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 7/29/2009 9:52 AM
I think the brightness change is due to the way the RDLC viewer is displaying it? Because if you export to PDF the color looks normal.

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 8/3/2009 11:20 PM
Hi Andy,

I'm not familiar with the SharpZip libriary. How and where in your code would you use it? Please let me know. Thanks.

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 8/6/2009 1:12 PM
Worth noting here is that the "screendump" cannot contain images that comes from a cross-domain site. If they do you won't' be able to read the pixels, and this approach breaks. Yet another reason for proper printing support in Silverlight.

Also note that Joe's PngEncoder is slooooow. Nikola optimized this code A LOT: http://blogs.msdn.com/nikola/archive/2009/03/04/silverlight-super-fast-dymanic-image-generation-code-revisited.aspx

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 10/5/2009 6:06 AM
could you please publish the vb project also.

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 10/19/2009 10:41 PM
This project might be helpful for printing: http://silverpdf.codeplex.com

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 11/12/2009 1:06 PM
Hi,

I'm creating a very simple drawing program and I would like to be able to save the drawing to my web server (via JSP). I am using some of your code:

Stream pngStream = imageData.GetStream();
byte[] binaryData = new Byte[pngStream.Length];
long bytesRead = pngStream.Read(binaryData, 0, (int)pngStream.Length);

string base64String = System.Convert.ToBase64String(binaryData, 0, binaryData.Length);

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://myserver.com/scripts/GetImage.jsp?" + base64String));

but, I get an exception on the HttpWebRequest call.

System.UriFormatException: [net_uri_SizeLimt]

Any ideas?

Frank

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 11/12/2009 5:55 PM
Hi Frank,

You wouldn't be able to fit an image that large in a Querystring variable. Querystrings are limited to 2 kb in size if I remember right. So you'll need to put it in a form variable like the demo shows.

Also note that the image data is NOT being compressed in the sample. You should take a look at the Image Tools on Codeplex - http://imagetools.codeplex.com/ - to get a better compression out of it.

-Andy

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 11/14/2009 1:42 AM
Hi,

I have a problem with the DOM access:
HtmlElement txtPNGBytes = document.GetElementById("txtPNGBytes");

The txtPNGBytes html element always null but is exists in the aspx page:
...




...

Do you know anybody what is the problem?

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 1/20/2010 2:29 AM
I am currently working on Silverlight application and have issues creating report. Any help on creating a simple report with code (may be to print 2 columns from one table) would be highly appreciated.

Thanks for your help.

Prasad

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 2/17/2010 12:51 AM
Hello Guys

I am having problem with Height of the control that i have to print
public int Height
{
get
{
return _height;
}
set
{
if (_init)
{
OnImageError("Error: Cannot change Height after the EditableImage has been initialized");
}
else if ((value <= 0) || (value > 2047))
{
OnImageError("Error: Height must be between 0 and 2047");
}
else
{
_height = value;
}
}
}

If value is greater then "value > 2047"
Then it creates a problem while create string date from Byte array.

Is any one facing same issue.

Please let me know what is the best solution for same.

Right now i m going to try for split the array

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 2/17/2010 12:52 AM
Hello Guys

I am having problem with Height of the control that i have to print
public int Height
{
get
{
return _height;
}
set
{
if (_init)
{
OnImageError("Error: Cannot change Height after the EditableImage has been initialized");
}
else if ((value <= 0) || (value > 2047))
{
OnImageError("Error: Height must be between 0 and 2047");
}
else
{
_height = value;
}
}
}

If value is greater then "value > 2047"
Then it creates a problem while create string date from Byte array.

Is any one facing same issue.

Please let me know what is the best solution for same.

Right now i m going to try for split the array

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 5/27/2010 11:18 AM
Anybody have this solution in VB.net?

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 6/6/2010 10:05 PM
very cool.
Devsolution.net

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 8/11/2010 4:05 AM
First i want just to thank you about this code. When i try this exemple i found a problem when you set the width of canvas = 1300 and the height =1300. The project doesn't work. Have you a solution for this problem. thanks :)

bankruptcy    By Anonymous on 5/31/2013 8:06 AM
bankruptcy-lawarticles.com
A good blog always comes-up with new and exciting information and while reading I have feel that this blog is really have all those quality that qualify a blog to be a one.I have express a few of the articles on your website now, and I really like your style of blogging.

Re: "Printing" in Silverlight 3 with WriteableBitmap    By Anonymous on 6/21/2013 6:40 AM
hey i liked this site . you have shared with me a large set of games and art zones. i will definitely love to try them all. thank you for making such a good read for me. will love to read it and share more.
more here


Title:
Comment:
Add Comment   Cancel 
Module Border Module Border
Module Border
  Subscribe
Module Border
RSS   Twitter
Module Border Module Border
Module Border
  Diversions
Module Border

TALKING RAGDOLL
This Windows Phone 7 App was created using Silverlight, the  Physics Helper Library,  and the Farseer Physics Engine. It gets interesting when you import your friends photos and have your way with them!

MORE INFO



DROPPYPOP
This Windows Phone 7 game was created using Silverlight, the  Physics Helper Library,  and the Farseer Physics Engine.
DEMO

MORE INFO



BOSS LAUNCH
This physics game won first place in the Server Quest Contest. Created using Silverlight 2, the Physics Helper Library,  and the Farseer Physics Engine.
PLAY IT

MORE INFO



DESTROY ALL INVADERS
A scrolling shooter game where the objective is to destroy the invading UFO's flying over a neighborhood of your choosing. Imagery provided by Microsoft Virtual Earth. Created using Silverlight 2.
PLAY IT

INFO AND CODE



PHYSICS HELPER DEMOS
These demos were created for the Physics Helper Library, which makes it easy to create physics games and simulations using Expression Blend, Silverlight, and the Farseer Physics Engine.
PLAY IT

INFO AND CODE



HOOK SHOT
This little basketball game took first place in the TeamZoneSports Silverlight Contest. Created using Silverlight 2 and the Farseer Physics engine.
PLAY IT

MORE INFO



SORT THE FOOBARS
A game where you need to sort the good foobars from the bad ones. Created using Silverlight 2 and the Farseer Physics engine.
PLAY IT

MORE INFO



POLYGON PHYSICS DEMO
A demo showing polygon physics where the user draws physics objects with the mouse. Created using Silverlight 2 and the Farseer Physics engine.
PLAY IT

MORE INFO



SILVERLIGHT ROCKS!
Destroy the asteroids before they destroy your ship! Created using Silverlight 2.
PLAY IT

INFO AND CODE



FISH GAME
A simple game of harpoon-the-fish. Written using the AJAX Sprite Toolkit.
PLAY IT

INFO AND CODE

Module Border Module Border
Module Border
  Search_Blog
Module Border
Module Border Module Border
Module Border
  Blog_Archive
Module Border
Module Border Module Border
Copyright (c) 2014 andy.beaulieu.com - Login