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 https://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).