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 »

  1. Nice post. looking forward to other posts reg XPS Docs.

    Comment by ravinash85 | January 2, 2008 | Reply

  2. will this code work with xaml document which has some Canvas in it used for Silverlight

    Comment by marce | January 10, 2008 | Reply

  3. Yes, you can have a Canvas inside the FlowDocument. However, the Canvas cannot be the root element.

    If you need to display a FlowDocument inside a Canvas, you may want to look at FlowDocumentPageViewer.

    Comment by roecode | January 10, 2008 | Reply

  4. How doit without DialogBox?

    Comment by Omar | May 1, 2008 | Reply

  5. It sounds like you want to use the LocalPrintServer to get a default printer. Something like the following would work.

    PrintQueue printQueue = null;

    LocalPrintServer localPrintServer = new LocalPrintServer();

    // Retrieving collection of local printer on user machine
    PrintQueueCollection localPrinterCollection =
    localPrintServer.GetPrintQueues();

    System.Collections.IEnumerator localPrinterEnumerator =
    localPrinterCollection.GetEnumerator();

    if (localPrinterEnumerator.MoveNext())
    {
    // Get PrintQueue from first available printer
    printQueue = (PrintQueue)localPrinterEnumerator.Current;
    }
    else
    {
    // No printer exist, return null PrintTicket
    return null;
    }

    Comment by roecode | May 1, 2008 | Reply

  6. I am having compilation errors with XamlTemplatePrinter. What reference do I need and where do I get the library?

    Thanks,
    Linda Rawson

    Comment by Linda Rawson | May 13, 2008 | Reply

  7. About your compilation errors. I cannot immediately tell why that would be. I should be able to help if you posted some of the errors you see.

    Comment by roecode | May 14, 2008 | Reply

  8. Hi,

    Im having compilations errors with XamlTemplatePrinter too.
    “the name XamlTemplatePrinter dont exist at current context”

    Where is defined XamlTemplatePrinter?

    Thanks,

    Checho

    Comment by Checho | May 19, 2008 | Reply

  9. The XamlTemplatePrinter is a class that contains the static methods GetPrintXpsDocumentWriter, RenderFlowDocumentTemplate, and PrintFlowDocument.

    Sorry I did not spell this out explicitly here. I wrongly assumed readers would identify the nature of static methods being called here and package the methods in appropriate classes.

    Comment by roecode | May 19, 2008 | Reply

  10. upssss!!!

    Excuse my ignorance. I was a victim of my anxiety.

    Thank.

    Checho

    Comment by Checho | May 20, 2008 | Reply

  11. […] FlowDocument XAML to print XPS Documents – 1, 2, 3, 4, 5, 6 […]

    Pingback by Impresión de informes en WPF « Carliños Blog | August 11, 2008 | Reply

  12. Guess I’m idiot too because I still cannot get the XamlTemplatePrinter part compiled, same as Post #8.. and not sure what you mean on #9, could you tell me more explicitly what I need to do? Also, I want to take this to build reports, so it’s important for me to figure your code out! 🙂

    Thanks,
    Jennifer

    Comment by Jennifer Overholt | October 10, 2008 | Reply

  13. I would be happy to help, drop me an instant message at roe_code@hotmail.com

    Comment by roecode | October 10, 2008 | Reply

  14. #8 + #12: I had the same problem, but found out, thanks to #9, that XamlTemplatePrinter is the class where the methods reside. So just name your class XamlTemplatePrinter and it will work.

    Comment by Morten | March 14, 2009 | Reply

  15. Seems like there are a lot of questions about this topic. A static method in a class means you do not have to instantiate the class. Just name your class XamlTemplatePrinter and put the static methods inside that class.

    Comment by roecode | March 16, 2009 | Reply

  16. Your example is very well written. That is how it should be with new technologies. Looking forward for the next article.

    Comment by csbjunior | July 21, 2009 | Reply

  17. No point really in having those xps files, they are a bit rubbish 🙂
    Save the trouble and save them as open documents

    Comment by xps to pdf | November 21, 2010 | Reply

  18. Hi,
    You are getting the raw data from flow document and send it to printer. The same way, i want to pass my pdf file to xps document printer. Do you have any idea about this.

    Comment by balaji | December 8, 2010 | Reply

  19. How to load FlowDocument into DocumentViewer, preview it and print it using DocumentViewer?

    Comment by Christopher | March 17, 2011 | Reply

  20. Please, help!

    Comment by Christopher | March 17, 2011 | Reply

  21. Or how to make preview for FlowDocument before printing?

    Comment by Christopher | March 17, 2011 | Reply

  22. How to do it without DialogBox?

    Comment by Wasuh | March 28, 2011 | Reply


Leave a comment