Running on Empty

The few things I know, I like to share.

Using FlowDocument XAML to print XPS Documents. (Part 1)

Introduction
In this series of articles I will demonstrate XAML techniques for creating XPS documents and FlowDocuments for reporting and printing in WPF applications. This first article will focus on creating a FlowDocument template using XAML.
XPS documents are the new XML paper specification for electronic paper. XPS will most likely not supplant PDF document formats for electronic media, but if you are already using WPF it makes sense to use the built in functionality provided by the .Net framework for XPS document viewing.
 

Creating a XAML FlowDocument.
The first step is to insert a new XML document into your WPF solution. Save the document with the XAML extension and replace the normal XML headers with the following.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Paragraph FontFamily="Arial">
    Hello World!
  </Paragraph>
</FlowDocument>

Nice and simple, we now have a useful start to the XAML template.
Now, you will want to make certain the build properties are set up correctly. Right click on your new XAML document and go to properties. Make the following changes.• Build Action-> Content

• Copy to Output Directory -> Copy Always

Your XAML template will now build to the output /Bin directory along with your executable.

 Creating a XAML FlowDocument printer.

First, create a method that will take our FlowDocument XAML and convert it into a DocumentPaginator.

public static IDocumentPaginatorSource RenderFlowDocumentTemplate(string templatePath)
{
    string rawXamlText = "";

    //Create a StreamReader that will read from the document template.
    using (StreamReader streamReader = File.OpenText(templatePath))
    {
        rawXamlText = streamReader.ReadToEnd();
    }
    //Use the XAML reader to create a FlowDocument from the XAML string.
    FlowDocument document = XamlReader.Load(new XmlTextReader(new StringReader(rawXamlText))) as FlowDocument;
    return document;
}

Next, create a method that will open the standard print dialog and return to us a valid printer.

public static PrintDialog GetPrintDialog()
{
    PrintDialog printDialog = null;

    // Create a Print dialog.
    PrintDialog dlg = new PrintDialog();

    // Show the printer dialog.  If the return is "true",
    // the user made a valid selection and clicked "Ok".
    if (dlg.ShowDialog() == true)
        printDialog = dlg;  // return the dialog the user selections.
    return printDialog;
}

Next, create a method that will write an XPS document.

private static void PrintDocumentPaginator(XpsDocumentWriter xpsDocumentWriter, DocumentPaginator document)
{
    xpsDocumentWriter.Write(document);
}

Finally, create a pair of methods that will get and print the FlowDocument.

private static XpsDocumentWriter GetPrintXpsDocumentWriter(PrintQueue printQueue)
{
    XpsDocumentWriter xpsWriter = PrintQueue.CreateXpsDocumentWriter(printQueue);
    return xpsWriter;
}

public static void PrintFlowDocument(PrintQueue printQueue, DocumentPaginator document)
{
    XpsDocumentWriter xpsDocumentWriter = GetPrintXpsDocumentWriter(printQueue);
    PrintDocumentPaginator(xpsDocumentWriter, document);
}

Bringing it together.
Create a print button on your application, place the following code in the Click method.

private void PrintButton_Click(object sender, RoutedEventArgs e)
{
    IDocumentPaginatorSource flowDocument = XamlTemplatePrinter.RenderFlowDocumentTemplate(Path.Combine(Environment.CurrentDirectory, "XamlDocumentTemplate.xaml"));

    PrintDialog flowPrintDialog = XamlTemplatePrinter.GetPrintDialog();
    if (flowPrintDialog == null)
        return;

    PrintQueue flowPrintQueue = flowPrintDialog.PrintQueue;
    XamlTemplatePrinter.PrintFlowDocument(flowPrintQueue, flowDocument.DocumentPaginator);
}

Run the application and press the print button, you will now be able to print Hello World to the selected printer. Not exactly very special, but in the few articles I will build upon the printing class to include dynamic templates, which will greatly increase the usefulness of this class.
As always feel free to comment or leave suggestions.

December 21, 2007 Posted by | C#, WPF, XAML, XPS | 22 Comments