Xamarin.Forms E-Z Print
It seems in the age of phones & tablets that are as powerful as computers used to be only a few years ago, with huge amounts of on-board storage and almost always on internet where anything can be looked up instantly, that the days of printing out documents would be over. Well, that’s not the case. There’s something to be said about holding a piece of paper in your hands to consume information from. In some ways, it’s reassuring the data one is looking at is real – not just some pixels on a screen. So it’s inevitable that we’ll be asked to add printing functionality to our apps one day … and if we’re creating a Xamarin.Forms app, there’s a very simple way of doing so. Let’s take a look at how it’s done!
Xamarin.Forms E-Z Print
First off I want to say that this solution may not be one size fits all. However, I have found it does work in the several use cases I have needed it for, and I wanted to share it with you. (If you just want to jump into some code, you can find a full demo solution on my GitHub here.)
Each operating system provides its own printing subsystem. The print engines on the respective platforms can do some pretty amazing things (as with UI graphics, you can go low-level & print in intricate detail). The problem arises when devising a way to give the print engine the content to print… AND ideally having that content be exactly the same type for both operating systems… AND providing the most flexibility for the layout of the content… AND requiring us to do the least amount of coding.
For example … do we need to do some custom drawing on each platform and print that out? Maybe create a PDF, and have the engine print that? The engine should probably be able to handle printing an image – so maybe we should take the content we want to print – convert it to an image & hand that over to be printed?
As you can imagine, there are any number of ways we can go about this. But the key is that we want to do it with the least amount of work possible – while providing the most flexibility as possible.
So what’s the easiest way to go about this? Our old friend HTML!
Make the OS Work For Us!
HTML … in a Xamarin-based app? Yup! But I’m not going to tell you we have to redo the whole thing as a Cordova app. Rather since each operating system supports the ability to print HTML from its version of a web view, and can do so with full formatting, images, color, etc … and since the Xamarin.Forms.WebView
is really only an abstraction over each of the OS’s respective native web views … all we need to do is to get what we want to print into HTML, and then let iOS and Android take care of the rest!
We’ll get full access to the printing system, just by passing some HTML – satisfying the “only generating one type of data” requirement!
Razor to the Rescue!
Now, what’s the easiest way to generate the HTML so that we can do the least amount of work, but provide the most amount of flexibility? Enter the Razor templating engine … yes, the same Razor that you used to create ASP.NET MVC views before you discovered the glamorous world of app development!
Turns out Xamarin has robust support of Razor templates – one can even supply images and apply CSS frameworks – and generating HTML from those templates really is a breeze. Xamarin has some good documentation on Razor templates within their apps, but let’s take a quick tour at what we need to do to generate some HTML.
The first thing we want to do is add the Razor template to the solution – put it into the PCL or Shared Project portion of the Forms project. The new Razor file can be found under “Text Templating” section in the “New File” dialog.
One of the cool things that’ll happen with this template is that it will be available to instantiate from within our PCL. And that means we can set the @model
property of the template … and of course that’s the property which is used as the data source when rendering the view.
As I mentioned above – another cool thing about the Razor templating within Xamarin – we can access static resources, such as images and CSS files. That means the resulting HTML can have some slick formatting to it without cluttering the template up, and if we’re using something like Bootstrap or Primer, without a lot of work on our part either.
The only thing to remember when using static assets is that they must be located in the platform project. On iOS, the images and CSS needs to go in the Resources
folder and have a build action of BundleResource
. On Droid, they need to reside in the Assets
directory, and have a build action of AndroidAsset
.
Here’s an example of a Razor template that has a System.Collections.List<T>
as the model, uses Primer as a CSS framework. On line 21, it loops through the data to do a basic display.
@using FormsEZPrint;
@using System.Collections.Generic;
@model List<EZPrintModel>
<!DOCTYPE html>
<html lang="en">
<head>
<!-- iOS - needs to reside in the "Resources" directory and have build action of "BundleResource" -->
<!-- Droid - needs to reside in "Assets" directory and have build action of "AndroidAsset" -->
<link rel="stylesheet" href="primer.css">
</head>
<body>
<div class="blankslate blankslate-spacious">
<h3>Xamarin Forms EZ-Print!</h3>
<p>Even works using the CSS frameworks like Primer!</p>
</div>
<div class="container">
<div class="columns">
@foreach (var m in Model) {
<div class="one-fifth column">
<span class="d-inline-block p-3 bg-red text-white">
@m.ModelName
</span>
</div>
<div class="four-fifths column">
<span class="d-inline-block p-3 bg-green">
@m.ModelDescription
</span>
</div>
}
</div>
</div>
</body>
</html>
So that’s all that’s needed for the template … but how does one actually generate the HTML from it? Even easier… Remember when I mentioned that we could instantiate the Razor template from our code? By doing that, setting its model property, and then telling the template to generate … that’s all that’s needed!
// New up the Razor template
var printTemplate = new ListPrintTemplate();
// Set the model property (ViewModel is a custom property within containing view - FYI)
printTemplate.Model = ViewModel.ezPrints.ToList();
// Generate the HTML
var htmlString = printTemplate.GenerateString();
Alright – with the HTML obtained – now’s the time to pop it into a Xamarin.Forms.WebView
. Again, super easy, nothing you haven’t seen elsewhere.
// New up the Razor template
var printTemplate = new ListPrintTemplate();
// Set the model property (ViewModel is a custom property within containing view - FYI)
printTemplate.Model = ViewModel.ezPrints.ToList();
// Generate the HTML
var htmlString = printTemplate.GenerateString();
// Create a source for the webview
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = htmlString;
// Create and populate the Xamarin.Forms.WebView
var browser = new WebView();
browser.Source = htmlSource;
You’ll notice that nothing is being added to any sort of layout anywhere … that’s because the WebView
is not being displayed at all. We’re just using it to hold the rendered HTML content, and then passing it to the platform’s printing engine. By doing it this way, we work around the situation where one sees a screen -> taps Print -> Sees a screen with a web view -> taps Print (again) … then finally sees the platform’s printing dialog. In other words – we’re avoiding seeing the web view!
Now we’re up the point where we need to make a handoff to the individual platform’s operating system. The easiest way is to use Form’s built-in DependencyService
.
Dependency Service Setup
To make it easy to get at the platform specific implementation of our print service – we’ll use the built-in DependencyService
(although we don’t have to … partial classes, other IoC, etc. would work as well).
The DependencyService
needs an interface to work with – so we’ll use this:
using Xamarin.Forms;
namespace FormsEZPrint
{
public interface IPrintService
{
void Print(WebView viewToPrint);
}
}
The big thing to notice here is that the Print
function is taking a Xamarin.Forms.WebView
as a parameter.
When the shared code needs to print – it will look something like the following to invoke the print service:
var printService = DependencyService.Get<IPrintService>();
printService.Print(browser);
We’re all set! Well, except for the platform specific implementation of printing that is…
iOS Implementation
The key to printing in iOS is to get the native implementation of
Xamarin.Forms.WebView
… which happens to be UIWebView
in iOS. Once that UIWebView
is in hand – then it’s a trivial matter to create the rest of the print job. Set some general parameters to the UIPrintInfo
for page layout, and then show the shared UIPrintInteractionController
– setting the UIWebView
as the print formatter.
The secret sauce to get the UIWebView
? Xamarin.Forms.Platform.iOS.Platform.CreateRenderer(view).NativeView
… that’s going to get us the native view, and then the rest is standard iOS code.
[assembly: Xamarin.Forms.Dependency(typeof(ApplePrintService))]
namespace FormsEZPrint.iOS
{
public class ApplePrintService : IPrintService
{
public ApplePrintService()
{
}
public void Print(WebView viewToPrint)
{
var appleViewToPrint = Platform.CreateRenderer(viewToPrint).NativeView;
var printInfo = UIPrintInfo.PrintInfo;
printInfo.OutputType = UIPrintInfoOutputType.General;
printInfo.JobName = "Forms EZ-Print";
printInfo.Orientation = UIPrintInfoOrientation.Portrait;
printInfo.Duplex = UIPrintInfoDuplex.None;
var printController = UIPrintInteractionController.SharedPrintController;
printController.PrintInfo = printInfo;
printController.ShowsPageRange = true;
printController.PrintFormatter = appleViewToPrint.ViewPrintFormatter;
printController.Present(true, (printInteractionController, completed, error) => { }); }
}
}
That takes care of iOS … let’s look at Android …
Droid Implementation
Again, the whole point here is to get at Android’s native version of WebView
… Android.Webkit.WebView. Platform.CreateRenderer()
is our friend again in this situation!
[assembly: Xamarin.Forms.Dependency(typeof(DroidPrintService))]
namespace FormsEZPrint.Droid
{
public class DroidPrintService : IPrintService
{
public DroidPrintService()
{
}
public void Print(WebView viewToPrint)
{
var droidViewToPrint = Platform.CreateRenderer(viewToPrint).ViewGroup.GetChildAt(0) as Android.Webkit.WebView;
if (droidViewToPrint != null)
{
// Only valid for API 19+
var version = Android.OS.Build.VERSION.SdkInt;
if (version >= Android.OS.BuildVersionCodes.Kitkat)
{
var printMgr = (PrintManager)Forms.Context.GetSystemService(Context.PrintService);
printMgr.Print("Forms-EZ-Print", droidViewToPrint.CreatePrintDocumentAdapter(), null);
}
}
}
}
}
We have to jump through some more hoops to get at the Android WebView
– but we can do it nonetheless. One BIG, BIG, BIG caveat however … this will only work for API level 19 and up.
Conclusion
And there you have it … a relatively easy and painless way to implement printing within Xamarin.Forms. The most difficult part is designing the template to get the exact look that you want … but with the ability to include CSS frameworks and static images, even doing that is easy. A full demo project can be found on GitHub here. Who would have thought printing could be so easy?!?