共用方式為


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).

Comments

  • Anonymous
    January 24, 2007
    The comment has been removed

  • Anonymous
    January 24, 2007
    Ohh.. Images are printed when using XamlPackage instead.

  • Anonymous
    February 08, 2007
    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.

  • Anonymous
    April 18, 2007
    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?

  • Anonymous
    May 29, 2007
    how copy image and text into flowDocument???