Printing Contents of WPF RichTextBox


I was recently playing with code to print contents of WPF RichTextBox control. The SDK has a code sample on this at http://msdn2.microsoft.com/en-us/library/aa970917.aspx


            // Print RichTextBox content


            private void PrintCommand()


            {


                PrintDialog pd = new PrintDialog();


                if ((pd.ShowDialog() == true))


                {


                    //use either one of the below     


                    pd.PrintVisual(richTB as Visual, “printing as visual”);


                    pd.PrintDocument((((IDocumentPaginatorSource)richTB.Document).DocumentPaginator), “printing as paginator”);


                }


            }



Using PrintDialog.PrintDocument() API as recommended in this sample does not produce good print output. The main reason being that page margins are not set correctly. The DocumentPaginator class does expose a PageSize property. But it does not have a margin property equivalent.


After little bit of poking around, I have finally found something that works. But it is very hard to discover to say the least. The below code sample shows how to use the XpsDocumentWriter class and its synchronous Write() API which accepts a DocumentPaginator object.


            // Serialize RichTextBox content into a stream in Xaml or XamlPackage format. (Note: XamlPackage format isn’t supported in partial trust.)


            TextRange sourceDocument = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);


            MemoryStream stream = new MemoryStream();


            sourceDocument.Save(stream, DataFormats.Xaml);


 


            // Clone the source document’s content into a new FlowDocument.


            FlowDocument flowDocumentCopy = new FlowDocument();


            TextRange copyDocumentRange = new TextRange(flowDocumentCopy.ContentStart, flowDocumentCopy.ContentEnd);


            copyDocumentRange.Load(stream, DataFormats.Xaml);


 


            // Create a XpsDocumentWriter object, open a Windows common print dialog.


            // This methods returns a ref parameter that represents information about the dimensions of the printer media.


            PrintDocumentImageableArea ia = null;


            XpsDocumentWriter docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia);


 


            if (docWriter != null && ia != null)


            {


                DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocumentCopy).DocumentPaginator;


 


                // Change the PageSize and PagePadding for the document to match the CanvasSize for the printer device.


                paginator.PageSize = new Size(ia.MediaSizeWidth, ia.MediaSizeHeight);


                Thickness pagePadding = flowDocumentCopy.PagePadding;


                flowDocumentCopy.PagePadding = new Thickness(


                        Math.Max(ia.OriginWidth, pagePadding.Left),


                        Math.Max(ia.OriginHeight, pagePadding.Top),


                        Math.Max(ia.MediaSizeWidth – (ia.OriginWidth + ia.ExtentWidth), pagePadding.Right),


                        Math.Max(ia.MediaSizeHeight – (ia.OriginHeight + ia.ExtentHeight), pagePadding.Bottom));


                flowDocumentCopy.ColumnWidth = double.PositiveInfinity;


 


                // Send DocumentPaginator to the printer.


                docWriter.Write(paginator);


            }


You might wonder why I need to copy the original RichTextBox’s FlowDocument and then send the cloned FlowDocument to XpsDocumentWriter. This is necessary because there is no way to support simultaneous editing and printing of the original FlowDocument in RichTextBox. Without confirming what is happening behind the scene here (so take this with a grain of salt), during the print operation XpsDocumentWriter needs to paginate through the content.  This means you need to suspend RichTextBox’s layout update until the print is complete. Of course, the RichTextBox needs to be UI responsive during printing, so this is not an option.


(Note that it does not matter whether the printing is done using the synchronous Write() method or asynchronous WriteAsync() method on XpsDocumentWriter).

Comments (5)

  1. Hi,

    Thanks for the article. I have however two problems with the code:

    1. Images is not printed.

    2. Max doesn’t work with my settings. Since pagePadding is NaN, the margin will also be NaN (witch results in clipping).

    Ie: Math.Max(22, NaN) == NaN

  2. Ohh.. Images are printed when using XamlPackage instead.

  3. Prajakta Joshi says:

    Yes, images are saved only in XamlPackage format. So you are right about (1). I was using this code in a partial trust app, so I had to use Xaml format. (It is a limitation that images cannot be printed in partial trust. This is probably something for us to address in v.next.)

    About (2), you bring up a good case which is broken in this code. You will have to check for NaN page padding values, and convert them to 0.

  4. Eli7 says:

    Thanks – the code works well. However, from a user point of view, it is a bit awkward and very confusing to have to use two Print Dialogs in order to print something.

    Is there any way to get an PrintDocumentImageableArea object from the first Print Dialog?

  5. wallas says:

    how copy image and text into flowDocument???