If you think that it is not possible to export arbitrary WPF (and Silverlight) UI elements to PDF, you'll be very happy to learn that Telerik makes this possible! We've had a pretty neat PDF library for a while now (RadPdfProcessing), which enables you to create and export PDF documents. The library doesn't have a built-in UIElement-to-pdf conversion because it is platform independent, and it knows nada about visuals, but it is a matter of simple coding to get you there.

You can now export any visual element to PDF. There is no more need to export the page as an image​ to get the same appearance. Let me explain how the magic works.

The demo I’'m going to walk you through contains an ExportHelper class and its ExportToPdf method. The method requires a UIElement that should be exported and a FixedContentEditor object onto which the UI element will be drawn (the drawing surface). The FixedContentEditor is a class from the Telerik PdfProcessing library, and it works in a manner similar to a Canvas element. When you use a Canvas, you add visual children and position them at certain absolute coordinates. With the FixedContentEditor, you move the position from which you will start drawing and then you draw something (DrawRectangle, DrawText and so on).

ExportingAGrid1

Here's how the conversion works: the goal is to convert any Xaml primitive to Pdf instructions. For example, if you want to render an Ellipse, you need to draw an ellipse (DrawEllipse method of the FixedContentEditor). Simply create a class called EllipseRenderer that knows how to do this, set the fill, set the stroke and draw an ellipse onto the drawing surface. Same principle goes for a TextBlock element; create a TextBlockRenderer that sets the fill, sets the font size and draws some text. These are our concrete renderers that handle concrete UI elements.

internal class EllipseRenderer : UIElementRendererBase
{
  internal override bool Render(UIElement element, PdfRenderContext context)
  {
    Ellipse ellipse = element as Ellipse;
    if (ellipse == null)
    {
      return false;
    }

    using (context.drawingSurface.SaveGraphicProperties())
    {
      SetStroke(context, ellipse.StrokeThickness, ellipse.Stroke, ellipse.ActualWidth, ellipse.ActualHeight);
      SetFill(context, ellipse.Fill, ellipse.ActualWidth, ellipse.ActualHeight);

      if (context.drawingSurface.GraphicProperties.IsFilled || context.drawingSurface.GraphicProperties.IsStroked)
      {
        context.drawingSurface.DrawEllipse(new Point(ellipse.ActualWidth / 2, ellipse.ActualHeight / 2), ellipse.ActualWidth / 2, ellipse.ActualHeight / 2);
      }
    }

    return true;
  }
}

We get great separation of concerns where renderers do not know about each other, but are still able to work together. Here is an image that depicts the flow of the drawing process:

ExportingAGrid12

There is a PdfRenderer class that serves as the central point of the system (or a facade). It has a Render method that accepts the UIElement that should be exported, and it has a collection of all concrete renderers. The PdfRenderer simply iterates and finds the first renderer that can export the UI element. The concrete renderer takes care of everything else; if necessary, it will call the Render method of the PdfRenderer recursively in order to propagate the export of its visual children. For example, this is how the PanelRenderer works: it draws a rectangle if the Panel has a Background, and it delegates the conversion of its children to the PdfRenderer object.

Here we see a ​RadChartView in a WPF Application:ChartViewInWpfApplication

And here we can see how it looks inside a PDF reader:
ChartViewInPdfReader

This is great because it is very flexible and highly customizable. We have created several renderers you can use, and if you need to, you can modify the code of any of them. In need, you can create a custom concrete renderer by inheriting the UIElementRendererBase class and implementing the Render abstract method.

We have created an SDK sample which demonstrates the approach. I hope you can’t wait to try it out and share with us what export capabilities you would love to see in Telerik UI for WPF.


Petar Marchev
About the Author

Petar Marchev

Petar Marchev is a developer in the Telerik XAML Team. Petar has a passion for desktop applications development, code optimizations and multithreaded programming. He is an audiophile and enjoys music from his full range speakers and tube amp.

Related Posts

Comments

Comments are disabled in preview mode.