Latest Tweet:
  • Loading...

One of the frequently asked questions on forums, blogs and by conference attendees is how implement printing in Silverlight applications. Out of the box Silverlight 2 does not have any printer specific APIs, and it’s up to you as the developer to figure out how to deal with printing. If you use the print button in Internet Explorer 7 to print you’ll get a simple rendering of the page. If you try to print from Firefox or Safari you’ll get nothing but a blank page. Printing is a core requirement in many business applications and something you need to be addressed if you’re going to build a Silverlight 2 application. In this blog post I’m going to cover different strategies for printing, and show how we can use the HTML Bridge combined with some of the new features in ASP.NET AJAX 4.0 to implement printing from a Silverlight 2 application.

Print Preview of Silverlight content in IE7

Print preview from the Dive Log application. The Silverlight control is stretched since the container is sized to 100% width and height, and the print preview is in portrait mode. This is probably not what the user wants to print anyway.

Printing strategies

When dealing with printing in Silverlight, or perhaps in web applications in general, you have two main options; generating printouts server side or client side.

When building applications you often want to have printout reports that differ from the actual markup making up the UI of the application. In those cases a common solution for printing is to open a pop-up window that contains a server generated report ready to print. The report can be in any format depending on your requirements. You can show a PDF or XPS document, a Word document, or just a plain HTML document. When implementing server side generation of reports you are not limited to what’s available in the browser, and can choose whatever technology you want. You can use plain ASP.NET to generate HTML, or you can use a more advanced reporting solution such as SQL Reporting Services to generate PDF documents on the fly. The drawback of using server side generated reports for printing is that the user has to click a separate print button in your application to bring up the report. If the user simply clicks the print button in the browser toolbar they will get whatever HTML is available in the browser. This breaks the default user interaction models and is something we should try to avoid if possible. You’re also depending on server side resources to generate the report instead of leveraging the power of the client computer.

Client side printing simply means using the built-in functionality of the browser to provide printing in your application. For most content-centric sites, like blogs and news papers, this is the most obvious solution as the entire HTML is already available in the browser. However, you probably don’t want to print things like navigation elements or large header graphics. To solve this all modern browsers supports separate CSS style sheets for print. This enables the developer or designer to customize the look and feel of the page when printed. You can use CSS to hide navigation elements, large graphics, or other parts of the page you don’t want to include in your printout. This is great for HTML pages, but how does this apply to Silverlight 2 applications?

Silverlight is embedded onto the page using an object-tag, normally sitting inside some div-container. This allows us to apply CSS styles against the Silverlight container. In our CSS print style sheet we simply set the visibility to none to hide the Silverlight content when printing the page. At the same time we can change the visibility of another div-container that was hidden in the screen style sheet, but is now visible when printing the page. This enables us to swap visibility between the Silverlight application and other HTML content sitting behind it. Now we just need to populate this HTML element with meaningful content when printing. The way you interact between Silverlight 2 and the browser is through the HTML Bridge. The HTML Bridge lets you invoke JavaScript functions or manipulate the HTML-DOM from managed code in Silverlight 2. Using this API we can dynamically generate HTML on the client to populate the print div-container. The visibility and style of the printout is controlled by the print style sheet. The next question is how to best generate HTML from Silverlight 2. You have a couple of options;

  • Concatenate strings using the StringBuilder
    The perhaps most straight forward approach would be to write code that builds up a big HTML string in Silverlight 2, and then set the InnerHtml property of the container using the HTML Bridge. This would work just fine, but string concatenation is hard to maintain and easily leads to spaghetti code.
  • Use LINQ to XML to generate HTML
    In many cases this would be a good option as you could easily write LINQ queries against your objects, and then use LINQ to XML to dynamically build up the HTML document. This would be cleaner and easier to test and maintain than concatenating strings. The problem with generating the printout through code is that the look and feel of the printout is part of the user experience, and something that should be owned by the UX team and not the developers. Having to change LINQ to XML code to update report is not very flexible or designer friendly.
  • Use Client-side HTML controls for Silverlight 2
    Jeff Wilcox, the guy behind the Silverlight 2 Testing Frameworks, wrote an interesting post a few days back about a Client-side HTML controls library for Silverlight 2. This is something Jeff wrote for the testing framework to generate the test-report. He had the same problem of dynamically generating HTML from Silverlight, and solved it by implementing an abstraction layer on top of the HTML Bridge to build a client-side HTML control library. The library is built by extending the HtmlElement class to build higher level elements like Button, TextBox, Div, Headings, and even complex controls like a progress bar. This gives you an ASP.NET-like programming model running on the client. You can write clean, testable code against the controls, but you still have to build your reports through code. This makes things hard for your designers. The controls are currently only available through the testing framework, something that could make it hard to use in a production environment. But Jeff definitely got some interesting ideas around creating HTML content on the client that is well worth checking out.
  • ASP.NET AJAX 4.0 Client Templates
    An new and really interesting way of generating HTML on the client is using the new Client Templates introduced in the ASP.NET AJAX 4.0 preview release. The client templates let you define HTML fragments containing JavaScript binding expressions. The templates can later be applied to a JavaScript array to generate HTML on the client. This allows you to keep the HTML outside of your Silverlight code, and the only thing you have to pass over the HTML Bridge is the objects you want to use for your printout. This gives your designers the flexibility to work on the HTML and CSS for the printout while keeping your Silverlight 2 code nice and tidy. This is the approach I’ve taken for adding print support to the Dive Log example application.

Printing using ASP.NET AJAX 4.0 Client Templates

One of the really interesting features of the ASP.NET AJAX 4.0 preview is client templates. This enables you to write HTML fragments containing JavaScript binding expressions. The templates can be applied to JavaScript arrays to generate HTML on the client. I would recommend checking out Bertrand Le Roy’s two part blog post for a good introduction to client templates. Scott Hanselman also did a post showing how to use client templates together with jQuery to build dynamic HTML in an AJAX application. The October issue of MSDN Magazine also got a good article about new AJAX support for data driven web sites. The ASP.NET AJAX 4.0 is preview technology and things are likely to change. However the client template script is just a 50KB JavaScript file so using it shouldn’t make a big impact on your application.

The printing user experience I want to achieve for the Dive Log application is really simple. I don’t want to add a print button to the Silverlight 2 interface. Instead all printing should be done through whatever functionality is available in the browser. Most likely by clicking the print-button in the toolbar, or going to the file menu and selecting print or print preview. The printout shouldn’t contain the Silverlight 2 user interface. Instead it should contain a HTML table listing all logged dives.

The first thing we need to do is include two CSS style sheets, one for print and one for screen. The style sheets are responsible for toggling between showing/hiding the Silverlight 2 container and the print container.

Seperate style sheets for print and screen.

I use the ASP.NET Silverlight Control to render the HTML needed to show the Silverlight 2 application. Underneath the Silverlight control I have a separate HTML-div that will be the container for the dynamically generate print HTML.

Silverlight 2 container

The most interesting part of the print implementation is the client template used to generate the HTML. The meat of the template is a plain HTML table with columns for each of the fields we want to use from the dive log. The body of the table is marked with a special CSS class to indicate that this is a template, and the columns contain JavaScript binding expressions.

ASP.NET AJAX 4 Client Template

To populate the template with data we need some JavaScript. The script is kept in a separate file to keep things nice and clean. The script is responsible for instantiating the template based on the HTML markup. The script also registers a custom markup extension for the format expression used to bind the date column. This shows some of the power of using JavaScript binding expressions, and the extensibility of the client template framework. The final piece of JavaScript is a simple function to bind data against the template. The data is passed in as a JSON string from the Silverlight 2 application, and is de-serialized before the template is applied to the JavaScript array.

JavaScript to populate Client Template (available in seperate download)

The final piece of code needed is the Silverlight 2 code responsible for updating the print template with data. This is done by simply invoking the updatePrint-function passing in a list of Dive object serialized as JSON. The code is added to the View Model and is executed every time Dive Logs are retrieved from the server. The list of dives is serialized to JSON using a simple extensions method.

Using the HTML Bridge to update the print container.

When we run the application and click the print-preview button we no longer get a stretched Silverlight 2 user interface. Instead we get a nice HTML table listing all our logged dives. Since we’re using standard HTML and CSS to do printing it now works in all browsers, and not just IE7 which is the only one currently supporting printing of Silverlight 2 content.

Print preview with custom HTML instead of Silverlight 2 content.

Summary

Printing support is a key feature of most line of business application. Unfortunately this isn’t one of the strong sides of Silverlight 2. Hopefully this will improve in future versions of Silverlight. Microsoft hasn’t made any announcements, but I hope for better support for rich text and text layouts. WPF already has great support for documents, and hopefully some of these features will make it into Silverlight. But for now we have to use existing web technologies to solve printing.

In many cases server side generated report will be a good option. Using the HTML Bridge you can easily bring up a pop-up window where you pass in URL parameters to render the report. However, in some cases you should consider providing a good client side printing experience. Hopefully this blog post have given you some ideas to how to implement printing in Silverlight, and at the same shown some of the power of combining Silverlight with existing AJAX technologies. I did run into some minor challenges using this approach. One of them was figuring out when to update the print section with new HTML. Internet Explorer has a onbeforeprint-event you can listen to, but that one is not available in any other browser. Another option would be to listen to mouseout-events to try to detect when the user is moving up to the toolbar (and potentially could be clicking the print button). Non of these techniques where reliable enough, so I decided to simply update the content every time it's persisted/loaded from the server. This could lead to some inconsistence if the user edits a dive and click print before saving it back to the server. How you deal with this would depend on your application and requirements.

One added benefit to this approach is that you could render the initial HTML content of the print container on the server using ASP.NET. This would give search engines like Google something to index, making your Silverlight 2 application searchable. The search engines wouldn’t execute your Silverlight or JavaScript code, so this would only work for the initial rendering of the page.

I will post the updated version of the Dive Log application and source code tomorrow when Microsoft, according to rumors, will be releasing Silverlight 2.

Monday, October 13, 2008 8:34:42 AM (W. Europe Daylight Time, UTC+02:00)
Hi,

Interesting post but only for a very limited scope. What if you want to print real graphics (vectors and stuff like that) ? I've made some testing with extracting the XAML as a string from the plugin, posting it back to the server through a WCF service and using the PrintVisual method to generate a PDF-file which is then streamed back to the client. Not an optimal solution and quite hard to implement. But I can't find any better solution.

Regards,
Tim
Tim Favour
Monday, October 13, 2008 8:42:56 AM (W. Europe Daylight Time, UTC+02:00)
Hi Tim,

You're right - this is no good excuse for Microsoft to not implement proper printing in Silverlight.

There are other solutions you could use on the client for graphs and graphics. You could use Google Charts and create image elements and embed them in the dynamically generated HTML. In Google Charts all the values are tunneled through the HTTP GET request and you get a standard image back (http://code.google.com/apis/chart/).

For "simple" graphics I would be interesting to see if someone comes up with a XAML to SVG converter, or XAML to HTML Canvas converter, one could use for printing...

But in many cases you have to do "workarounds" similar to what you've done - go back to the server for the heavy lifting.

Cheers,
Jonas
Monday, October 13, 2008 9:13:24 AM (W. Europe Daylight Time, UTC+02:00)
Hey Jonas,

I just published an article about server-side XAML to PNG generation. It's still very rough and doesn't react well on IIS at all for the moment. I want to investigate but no time for the moment. This is WPF XAML for the moment, but maybe the component could be extended to handle Silverlight XAML too.

http://blog.galasoft.ch/archive/2008/10/10/converting-and-customizing-xaml-to-png-with-server-side-wpf.aspx

Anyway, great article and I thought you might want to look at the article mentioned.

Greetings,
Laurent
Monday, October 13, 2008 5:25:46 PM (W. Europe Daylight Time, UTC+02:00)
Excellent article!!!
Monday, October 13, 2008 9:40:52 PM (W. Europe Daylight Time, UTC+02:00)
I just tried to print the article so I can take it home and make notes. Printing this article freezes in IE and in Firefox only prints the first page. How ironic? :)
Tuesday, October 14, 2008 9:47:49 AM (W. Europe Daylight Time, UTC+02:00)
utilizing javascript to snapshot the page and put it in clipboard, and get the image from clipboard in silverlight via javascript. will it work to the snapshot?
Tuesday, October 14, 2008 1:26:43 PM (W. Europe Daylight Time, UTC+02:00)
thank u r information

it very useful

u r blog Is very nice
Wednesday, October 15, 2008 11:33:21 AM (W. Europe Daylight Time, UTC+02:00)
@Bart - Haha - that's just sad... I need to fix a proper print style sheet for this blog (as well as add support for IE6). Will try to fix it over the weekend. Glad you liked the article!

@unrulendboy - I'm not sure if you can take screen shots using JavaScript, or embed binary data from a the clip board as an image in the HTML. But I'll do some research to see if it can be done :)

@Laurent - Thanks for sharing! Yeah, in many cases that might be a good solution, specially if you have allot of custom vector graphics. How well does your server rendering support third-party components? Something like a charting component for instance.

Sunday, October 19, 2008 9:20:41 PM (W. Europe Daylight Time, UTC+02:00)
Litt sånn praktisk om dykkeloggen:
Bottomtime er tiden fra du starter dykket (Time in), til du starter oppstigningen.
Tiden mellom time in og time out er dykketid.
Linn
Thursday, October 23, 2008 1:21:45 AM (W. Europe Daylight Time, UTC+02:00)
Linn: Du har rett - denne er veldig forenklet. Men jeg vurderer å lage en "skikkelig" online dykkerlogg basert på Silverlight, så da kommer jeg nok til å spørre deg om litt "faginput" ;)
Friday, January 16, 2009 8:01:15 PM (W. Europe Standard Time, UTC+01:00)
Hi Jonas,

Wow--great stuff (both in this article as well as your others)--I've learned a lot! I was able to come up with a client-side solution by pushing my print content (images from fax captures) out of SL isolated storage over the bridge into ASP.NET isolated storage and using an HTTP handler with the AJAX client template approach to dynamically construct the print view. This solution assumes you're hosting with ASP.NET tho, yes? My company may not want to stand up an IIS server, so the server round-trip solution may still be required (until SL3 has built-in print support?).

One thing I noted in Firefox with your dive demo--the SL content does not refresh when you return from the print view. I added an iframe to the aspx file to force the refresh.

Keep the good stuff coming!

Cheers,

Steve
Steve Cothern
Saturday, January 17, 2009 12:33:31 AM (W. Europe Standard Time, UTC+01:00)
Another possiblity as a stopgap -- just read about Google Gears; hmmm... looks promising! I'll investigate and report back.
Steve Cothern
Saturday, January 17, 2009 2:58:48 PM (W. Europe Standard Time, UTC+01:00)
Hi,

I'm not 100% I understand why you have involved ASP.NET Isolated Storage...

The solution I've demonstrated in this blog post does not involve any server component, and does not depend on you hosting the site in ASP.NET. The ASP.NET AJAX 4.0 libraries are pure client libraries that can be hosted from any server. The reason why I use ASP.NET AJAX 4.0 is to demonstrate how it can be combined with Silverlight, and how the client templates makes client side generation easier. There are other solutions as well.

Server side printing gives full control over the printing, and let you use technologies like SQL Reporting Services. This article describes this technique: http://www.silverlightshow.net/items/Building-a-Silverlight-Line-Of-Business-Application-Part-6.aspx

The basic idea is to use the HTML Bridge to dynamically generate an IFRAME element that you position ontop of your Silverlight content, and get the rendered report from the server.

Proper, client side printing is hopefully supported by Silverlight 3. If not, we still have to depend on HTML generation on client, or full PDF/Excell/HTML reports from the server.

As for Google Gears I'm not 100% how that would help you with printing. Google Gears basically gives you a client side database you can use for offline data. The benefit with Gears over Isolated Storage in Silverlight is that you get a database with tables where you can issue SQL queries. In Silverlight you have to come up with your own data storage solution (often serialized XML files).

Good luck, and thanks for the feedback!
Tuesday, January 20, 2009 7:09:35 PM (W. Europe Standard Time, UTC+01:00)
Hi Jonas,

I am using isolated storage to facilitate a better user experience. Core to our app is the decoding/encoding of a proprietary data format to something SL understands (in this case images as PNG). One of the design "desirables" for our app was to keep as much of the logic client-side as possible (the prototype I first implemented had the logic server-side, streaming the XAML to SL). The client-side solution now handles the decode/encode. To improve user experience, especially for large documents, the individual pages are cached in isolated storage.

This was all happening without getting ASP.NET involved until we wanted to introduce printing. From that point I leveraged the fact that ASP.NET also had the concept of isostore and HTTPhandler to generate dynamic HTML to retrieve the image pages from ASP.NET isostore (after they'd been transferred from SL isostore) rather than making another round-trip to the server. Maybe not the best/only way I am sure but I am something of a web nOOb :) (old desktop guy changing his ways).

I did in fact use the client templates to clean up the client side code(using your articles and examples from BertLR and ScottHa as guidance).

Perhaps I am wrong: is this printing approach not restricted to ASP.NET? I was under the impression that the HTTPhandler approach was unique to ASP.NET. As far as Gears goes, I thought that the LocalServer and ResourceStore classes might provide somewhat equivalent functionality to intercept the call to retrieve the images (database storage is not required).

Thanks again,

Steve
Steve Cothern
Wednesday, January 21, 2009 10:04:40 AM (W. Europe Standard Time, UTC+01:00)
You're right - if you hare making a round trip back to the server to generate the print-out using a n HTTP Handler you are limiting your self to ASP.NET. That beeing said - most web frameworks have similar concepts like a HTTPHandler so the same server side functionallity could be implemented in other platforms.

As for Gears it simply gives you a local database - if you only need it to cache data locally you could just as well use Silverlight Isolated Storage. The LocalServer is good for taking your entire application offline by caching HTML, CSS, JS etc. and to detect network state.

Btw: How do you generate a PNG on the client side based on XAML streamed from the server? Are you using a third-party PNG encoder/decoder class to do this client side?
Wednesday, January 21, 2009 6:45:37 PM (W. Europe Standard Time, UTC+01:00)
It's not XAML streamed from the server in the latest version -- it comes either as TIF or our proprietary format. For both cases I've implemented a decoder which then hands off to the PNG encoder that JoeStegman provided (with a few tweaks for palette images). The prototype did all the crunching server-side (which was nice cos it could be kept in native mode).

If I didn't want to get ASP.NET in the mix how can I generate DHTML that references the images in isostore? Sorry if this is a stupid question but as I mentioned I'm a relative newcomer to the web-world (yes I am old :) ).

Cheers,

Steve
Steve Cothern
Wednesday, January 21, 2009 6:55:33 PM (W. Europe Standard Time, UTC+01:00)
I see... And no - it's not a stupid question to ask if you can reference dynamically generated PNGs from DHTML. It's actually a really good questions. And as for your web-world issues I totally get you. Can be a big jump - and Silverlight makes things even more "strange" as it is a client side web technologies... The lines between web and client gets blured.

And now to answer your question: You can actually set HTML image tages to display a Base64 encoded image (inline image). This blog posts describes how a Flash guy used the technique to create a "screen shot" of a Flash drawing, and push it into JavaScript, and create a dynamic <img> tag to display it: http://danielmclaren.net/2008/03/use-javascript-to-take-a-screenshot-of-a-flash-movie

The second post is about actually embeding the Base64 encoded image: http://danielmclaren.net/2008/03/embedding-base64-image-data-into-a-webpage

However, according to that blog post there are some limitations. The author was able to embed a 100x100 PNG, but not a 200x200 PNG. So you need to do some more research about inline Base64 encoded HTML Images.

There is currently no way to reference a file in Isolated Storage from a <img> tag.

So yes - it should be possible to solve this using client-side technology only.
Wednesday, March 04, 2009 6:48:47 PM (W. Europe Standard Time, UTC+01:00)
Impressive! Thanks for the such an informative blog.
Tuesday, April 07, 2009 11:38:57 AM (W. Europe Daylight Time, UTC+02:00)
Hi! Could you please attach a source code of this example.
Thanks in advance!
Katya
Friday, April 17, 2009 9:31:35 AM (W. Europe Daylight Time, UTC+02:00)
The source code is included in the Dive Log demo app download:
http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/Silverlight%20MSDN%20Live%202009%20Demo.zip

Cheers,
Jonas
Monday, August 24, 2009 3:40:40 PM (W. Europe Daylight Time, UTC+02:00)
You should take a look at my printing pattern:
http://blog.mdk-photo.com/post/Printing-in-SL3-e28093-design-pattern.aspx

im making multiple instances of SL in the 650x950px dimmensions to make them fit a lettersized paper output...

somewhat enabling vector-printing in SL...

but it require some MacGyver-code to make it work ;-)
Monday, September 21, 2009 7:29:56 PM (W. Europe Daylight Time, UTC+02:00)
Hi,
Can't seem to find where style is switched to the right one. (style for print and style for screen.) I keep getting both view on my screen and print.
Any ideas?
Monday, November 09, 2009 11:51:22 AM (W. Europe Standard Time, UTC+01:00)
Good post, but have you thought about Silverlight 2 using CSS and ASP.NET AJAX before?
Tuesday, November 10, 2009 7:16:36 AM (W. Europe Standard Time, UTC+01:00)
That's great, I never thought about Printing in Silverlight like that before.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview
<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910