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

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

Вот мой класс Paginator:

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, который отображает данные (на данный момент это просто список строк).

Вот как я использую мой 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");
}

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

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

Следующий код использует 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, когда предварительный просмотр печати закрыт.

WPF не имеет встроенной функции предварительного просмотра, если вы хотите сделать предварительный просмотр, вам придется создать его самостоятельно. К счастью, это не должно быть так сложно.

У вас уже есть код нумерации страниц, который создает ваш DocumentPage объекты. Эти объекты содержат Visual, который вы можете пойти дальше и отобразить в вашем пользовательском интерфейсе.

То, что вы в конечном итоге будете делать, - это разбиение на страницы всего документа, собирая все DocumentPage объекты, а затем отображать их визуальные эффекты внутри прокрутки StackPanel или что-то подобное. Это одно и то же DocumentPage объекты, которые вы можете затем отправить на принтер.

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

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

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

XpsDocument doc = new XpsDocument("filename.xps", FileAccess.Read);
docViewer.Document = doc.GetFixedDocumentSequence();
doc.Close();

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

Вот как я получил это работает в NHaml

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

Где "загрузка" - это FlowDocument или IDocumentPaginatorSource, а вот DocumentDialog 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

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

Другие вопросы по тегам