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 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.
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?
- 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
Printing using ASP.NET AJAX 4.0 Client Templates
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.
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.
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.
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.
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.
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.