Как выполнить предварительный просмотр при использовании DocumentPaginator для печати?

Я использую класс, производный от DocumentPaginator (см. Ниже), для печати простых (только текстовых) отчетов из приложения WPF. У меня так, чтобы все печаталось правильно, но как мне заставить его делать предварительный просмотр перед печатью? У меня есть ощущение, что мне нужно использовать DocumentViewer, но я не могу понять, как это сделать.

Вот мой класс пагинатора:

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.Measure(PageSize);
        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; }
        set
        {
            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; } }
}

PageElementRenderer - это просто простой UserControl, который отображает данные (на данный момент это просто список строк).

Вот как я использую свой пагинатор строк

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");
}

Извините за дамп кода, но я не хотел пропустить что-то актуальное.

Ответов (6)

Решение

Так что я заработал после прочтения Pro WPF в C# 2008 (стр. 726).

Обычно классу DocumentViewer нужен файл XPS для предварительного просмотра его. Итак, я делаю следующее:

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
File.Delete(tempFileName); 
using (XpsDocument xpsDocument = new XpsDocument(tempFileName, FileAccess.ReadWrite))
{
    XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
    writer.Write(paginator);

    PrintPreview previewWindow = new PrintPreview
                                     {
                                         Owner = this,
                                         Document = xpsDocument.GetFixedDocumentSequence()
                                     };
    previewWindow.ShowDialog();
}

Я создаю диалоговое окно печати, чтобы получить размер страницы по умолчанию. Возможно, есть лучший способ сделать это. XpsDocument находится в ReachFramework.dll (пространство имен System.Windows.Xps.Packaging);

Вот окно PrintPreview.

<Window x:Class="WPFPrintTest.PrintPreview"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="previewWindow"
    Title="PrintPreview" Height="800" Width="800">
    <Grid>
        <DocumentViewer Name="viewer" 
                        Document="{Binding ElementName=previewWindow, Path=Document}" />
    </Grid>
</Window>

Код позади просто имеет свойство Document, например:

public IDocumentPaginatorSource Document
{
    get { return viewer.Document; }
    set { viewer.Document = value; }
}
XpsDocument doc = new XpsDocument("filename.xps", FileAccess.Read);
docViewer.Document = doc.GetFixedDocumentSequence();
doc.Close();

Код WinForm для предварительного просмотра печати:

PrintPreviewDialog PrintPreviewDialog = new PrintPreviewDialog();
PrintPreviewDialog.Document = PrintDocument;
PrintPreviewDialog.ShowDialog();

Ps: исходный постер прокомментировал, что это неправильный ответ для приложения WPF.

Я не думаю, что есть встроенный способ сделать это

Вот как у меня это работает в NHaml

var documentViewHostDialog = new DocumentDialog();
documentViewHostDialog.LoadDocument(load);
documentViewHostDialog.ShowDialog();

Где "load" - это FlowDocument или IDocumentPaginatorSource, а здесь - http://code.google.com/p/nhaml/source/browse/trunk/src/NHaml.Xps/DocumentDialog.xaml http: // code. google.com/p/nhaml/source/browse/trunk/src/NHaml.Xps/DocumentDialog.xaml.cs

Надеюсь, это сработает в вашем случае.

WPF doesn't come with any built-in Print Preview functionality, if you want to do a print preview, you're going to have to construct it yourself. Fortunately, it's shouldn't be that difficult.

You've already got the pagination code, which creates your DocumentPage objects. These objects contain a Visual, which you can go ahead and display in your UI.

What you'll end up doing, is paginating your entire document, collecting all the DocumentPage objects, then displaying their visuals inside of a scrolling StackPanel or something similar. These are the same DocumentPage objects that you can then send to the printer.

Следующий код использует MemoryStream для предварительного просмотра печати.

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;
XpsDocument.CreateXpsDocumentWriter(xpsDoc).Write(paginator);

documentViewer.Document = xpsDoc.GetFixedDocumentSequence();

Не забудьте использовать close () и удалить пакет из PackageStore, когда предварительный просмотр закрыт.