Skip to main content Skip to footer

Export Your UI to PDF

With a comprehensive PDF library like C1Pdf you can do many things. You can use the .NET version on a server to generate a custom report for any client. You can use it to print data from any UI control, say a datagrid or scheduler, as PDF. You can even save your entire UI as a PDF. In this blog post I will show you the necessary code to export your UI to PDF in any XAML-based platform. There are two strategies to exporting your UI to PDF. The first is to render the UI as an image and then write that image to a PDF. The second is to try and render each UI element as rich text in the PDF and only render custom controls and buttons as images. Let’s take a look at each strategy as it works in each Xaml platform.

Export UI as an Image

The most straightforward and most reliable approach to creating a PDF from your user interface is to render it as an image. Each platform as some basic level support for rendering UI elements as an image. So you can take advantage of these features to obtain an image, and then use the C1Pdf library to create the PDF with just a few lines of code. In Silverlight, the WriteableBitmap class can generate an image from any UI element. You can simply call the Render method on the bitmap, or pass in the element to its constructor (see below). The result is an image of any UI control. If you need to render an entire page of controls then you simply render the root; this is not limited to just one simple control.


// render element to image (Silverlight)  
var img = new WriteableBitmap(element, null);  

In WPF and WinRT XAML, you can use the RenderTargetBitmap class instead. While both platforms use the same class, the actual API is slightly different. In WPF, for instance, you must specify the size and pixel format before calling the Render method.


// render element to image (WPF)  
RenderTargetBitmap rtbmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);  
rtbmp.Render(element);  

Whereas in WinRT, we can render anything and get the size afterward. Also we have an asynchronous RenderAsync method available to us.


// render element to image (WinRT)  
var renderTargetBitmap = new RenderTargetBitmap();  
await renderTargetBitmap.RenderAsync(element);  

So now, let’s use this approach along with the C1Pdf library. Using C1Pdf is very simple it doesn’t require much explanation. I will let the code speak for itself. The code snippets below are intended for you to quickly copy and go, so I’ve included the file save picker logic as well.

Export UI to PDF (WPF and Silverlight)

The following code initializes a C1PdfDocument object, draws some UI element (named “content”) to the document, and saves it to the user’s machine.


// get stream to save to  
var dlg = new SaveFileDialog();  
dlg.DefaultExt = ".pdf";  
var dr = dlg.ShowDialog();  
if (!dr.HasValue || !dr.Value)  
{  
    return;  
}  

// create document  
var pdf = new C1PdfDocument(PaperKind.Letter);  
pdf.Clear();  

var img = new WriteableBitmap(CreateBitmap(content));  
// if Silverlight, use below instead  
// var img = new WriteableBitmap(content, null);  

pdf.DrawImage(img, pdf.PageRectangle, ContentAlignment.TopLeft, Stretch.None);  

// save document  
using (var stream = dlg.OpenFile())  
{  
    pdf.Save(stream);  
}  
MessageBox.Show(dlg.SafeFileName + " saved successfully!");  

The CreateBitmap method uses the RenderTargetBitmap class to generate a bitmap of the UI element. Since it requires us to first obtain the actual size of the element, I moved this code to a separate method which you will see below. If you are using Silverlight then you don’t need to perform this extra step.


public BitmapSource CreateBitmap(FrameworkElement element)  
{  
    int width = (int)Math.Ceiling(element.ActualWidth);  
    int height = (int)Math.Ceiling(element.ActualHeight);  

    width = width == 0 ? 1 : width;  
    height = height == 0 ? 1 : height;  

    // render element to image (WPF)  
    RenderTargetBitmap rtbmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Default);  
    rtbmp.Render(element);  
    return rtbmp;  
}  

Export UI to PDF (WinRT)

The code as it pertains to C1Pdf is identical no matter which platform you use. But due to the differences in WinRT versus WPF and Silverlight, the code in which to render an image and save it to the user’s machine is quite different. Here is an example of rendering some UI content to PDF in Windows Store apps (WinRT XAML).


FileSavePicker savePicker = new FileSavePicker();  
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;  
savePicker.FileTypeChoices.Add("PDF (*.pdf)", new List(new string[] { ".pdf" }));  
savePicker.DefaultFileExtension = ".pdf";  
StorageFile file = await savePicker.PickSaveFileAsync();  
if(file != null)  
{  
    // create document  
    var pdf = new C1PdfDocument(PaperKind.Letter);  
    pdf.Clear();  

    // set document info  
    var di = pdf.DocumentInfo;  
    di.Author = "ComponentOne";  
    di.Subject = "C1.Xaml.Pdf demo.";  
    di.Title = "Export UI to PDF";  

    // create a bitmap from the UI element  
    var img = await CreateBitmap(panel);  

    // draw bitmap to PDF  
    pdf.DrawImage(img, pdf.PageRectangle, ContentAlignment.TopLeft, Stretch.None);  

    // save PDF file  
    await pdf.SaveAsync(file);  

    // notify user it's saved  
    MessageDialog dialog = new MessageDialog("File saved!");  
    await dialog.ShowAsync();  
}  

As with WPF, I’ve moved the RenderTargetBitmap specific code to a sub method named CreateBitmap. If you work with RenderTargetBitmap much you will know the object model is very different almost for the sake of being different from WPF, but they’ve also made it asynchronous in WinRT which is very nice. Include using S_ystem.Runtime.InteropServices.WindowsRuntime;_ at the top of your code page.


async public Task CreateBitmap(FrameworkElement element)  
{  
    // render element to image (WinRT)  
    var renderTargetBitmap = new RenderTargetBitmap();  
    await renderTargetBitmap.RenderAsync(element);  
    var wb = new WriteableBitmap(renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight);  
    (await renderTargetBitmap.GetPixelsAsync()).CopyTo(wb.PixelBuffer);  
    var rect = new Rect(0, 0, renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight);  

    return wb;  
}  

Export UI as Text, not Image

So what if your UI is mostly text? You probably cringe at having to export it as an image just to get it into a PDF. But with C1Pdf, you have more options. You could use the rich API to draw text directly to the document, with support for paging, margins, etc. Or if you want something quick, you could try the DrawElement method which renders UI elements to PDF while maintaining text content in your UI. It’s not a reliable solution for rendering UI controls like progress bars, sliders and toggle buttons, but it’s ideal for rendering text. The output PDF has text the user can search and select on, and it’s smaller in file size as a result.


pdf.DrawElement(content, pdf.PageRectangle);  

Conclusion

In this article I’ve covered a couple topics. You can use the WriteableBitmap and RenderTargetBitmap classes to render any UI element to an image. This is just very practical stuff that has no dependency on ComponentOne. I’ve also shown how you can use the C1Pdf library to create a PDF that contains an image rendered from your user interface. I’ve included three complete samples you can download below.

ComponentOne Product Manager Greg Lutz

Greg Lutz

comments powered by Disqus