How to Print Preview when using a DocumentPaginator to print?


I'm using a class I've derived from DocumentPaginator (see below) to print simple (text only) reports from a WPF application. I've got it so that everything prints correctly, But how do I get it to do a print preview before printing? I have a feeling I need to use a DocumentViewer but I can't figure out how.

Here's my Paginator Class:

public class RowPaginator : DocumentPaginator
    private int rows;
    private Size pageSize;
    private int rowsPerPage;

    public RowPaginator(int rows)
        this.rows = rows;

    public override DocumentPage GetPage(int pageNumber)
        int currentRow = rowsPerPage * pageNumber;
        int rowsToPrint = Math.Min(rowsPerPage, rows - (rowsPerPage * pageNumber - 1));
        var page = new PageElementRenderer(pageNumber + 1, PageCount, currentRow, rowsToPrint)
                           Width = PageSize.Width,
                           Height = PageSize.Height
        page.Arrange(new Rect(new Point(0, 0), PageSize));
        return new DocumentPage(page);

    public override bool IsPageCountValid { get { return true; } }

    public override int PageCount { get { return (int)Math.Ceiling(this.rows / (double)this.rowsPerPage); } }

    public override Size PageSize
        get { return this.pageSize; }
            this.pageSize = value;
            this.rowsPerPage = PageElementRenderer.RowsPerPage(this.pageSize.Height);
            if (rowsPerPage <= 0)
                throw new InvalidOperationException("Page can't fit any rows!");

    public override IDocumentPaginatorSource Source { get { return null; } }

The PageElementRenderer is just a simple UserControl that displays the data (at the moment just a list of rows).

Here's how I use my Row Paginator

PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
    var paginator = new RowPaginator(rowsToPrint) { PageSize = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight) };

    dialog.PrintDocument(paginator, "Rows Document");

Sorry for the code dump, but I didn't want to miss something relevant.

9/10/2015 5:02:38 PM

Accepted Answer

So I got it working after reading Pro WPF in C# 2008 (Page 726).

Basically the DocumentViewer class needs an XPS file to present a print preview of it. So I do the following:

PrintDialog dialog = new PrintDialog();
var paginator = new RowPaginator(rowsToPrint) { PageSize = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight) };

string tempFileName = System.IO.Path.GetTempFileName();

//GetTempFileName creates a file, the XpsDocument throws an exception if the file already
//exists, so delete it. Possible race condition if someone else calls GetTempFileName
using (XpsDocument xpsDocument = new XpsDocument(tempFileName, FileAccess.ReadWrite))
    XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);

    PrintPreview previewWindow = new PrintPreview
                                         Owner = this,
                                         Document = xpsDocument.GetFixedDocumentSequence()

I'm creating the print dialog to get the default page size. There's probably a better way to do this. XpsDocument is in ReachFramework.dll (Namespace System.Windows.Xps.Packaging);

Here's the PrintPreview Window.

<Window x:Class="WPFPrintTest.PrintPreview"
    Title="PrintPreview" Height="800" Width="800">
        <DocumentViewer Name="viewer" 
                        Document="{Binding ElementName=previewWindow, Path=Document}" />

The code behind just has a Document property like so:

public IDocumentPaginatorSource Document
    get { return viewer.Document; }
    set { viewer.Document = value; }
11/19/2009 8:09:40 PM

The following code uses a MemoryStream to do a print preview.

MemoryStream stream = new MemoryStream();

Package package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);

var uri = new Uri(@"memorystream://myXps.xps");
PackageStore.AddPackage(uri, package);
var xpsDoc = new XpsDocument(package);

xpsDoc.Uri = uri;

documentViewer.Document = xpsDoc.GetFixedDocumentSequence();

Remember to use close() and remove package from PackageStore when the print preview is closed.

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow