使用MVVM从WPF View创建XPS

时间:2017-10-01 14:13:26

标签: c# wpf mvvm flowdocument xps

我在WPF中使用基于this的普通MVVM框架创建了一个应用程序。在工作流程结束时,我有一个长视图,其中包含需要打印到PDF的信息(我最初会将其打印到XPS,因为它是最简单的方法)。

我发现分页存在问题,可以使用FlowDocument修复here

我当前的问题是......如果我在视图中拥有所有信息,并且我不直接访问视图(因为这是MVVM),我该如何访问具有所有视图的视图实例信息并将其提供给FlowDocument?如何从视图模型实例中读取当前视图实例?

简而言之:我基本上都在考虑阅读<Grid> ... </Grid>的实例及其所有孩子和实际文本等... ...如应用程序中所示并将其提供给FlowDocument ......但我怎么能从视图模型中做到呢?

2 个答案:

答案 0 :(得分:0)

我不会尝试阅读视图 - 太复杂了。

视图组件不能有两个父母。

我最好制作一个应该进入XPS的视图的(多个)UserControl。

然后我会在FlowDocument中实现相同的UserControl,提供与DataContext相同的ViewModel

此致

答案 1 :(得分:0)

我认为您不需要使用任何可视对象来打印文档。主要是因为您可能不会知道最终用户将用于打印文档的页面大小或方向。

下面是我从一本书中获得的基本打印管理器,从这个基本概念我已经管理了我的所有打印需求。

using System.IO;
using System.Printing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

namespace MyTextEditor
{
    public class PrintManager
    {
        public static readonly int DPI = 96;
        private readonly RichTextBox _textBox;

        public PrintManager(RichTextBox textBox)
        {
            _textBox = textBox;
        }

        public bool Print()
        {

            PrintDialog dlg = new PrintDialog();

            if (dlg.ShowDialog() == true)
            {

                PrintQueue printQueue = dlg.PrintQueue;
                DocumentPaginator paginator = GetPaginator(
                    printQueue.UserPrintTicket.PageMediaSize.Width.Value,
                    printQueue.UserPrintTicket.PageMediaSize.Height.Value);

                dlg.PrintDocument(paginator, "TextEditor Printing");

                return true;
            }

            return false;
        }

        public DocumentPaginator GetPaginator(
            double pageWidth,
            double pageHeight)
        {
            TextRange originalRange = new TextRange(
                _textBox.Document.ContentStart,
                _textBox.Document.ContentEnd);

            MemoryStream memoryStream = new MemoryStream();
            originalRange.Save(memoryStream, DataFormats.Xaml);

            FlowDocument copy = new FlowDocument();

            TextRange copyRange = new TextRange(
                copy.ContentStart,
                copy.ContentEnd
                );

            copyRange.Load(memoryStream, DataFormats.Xaml);

            DocumentPaginator paginator = ((IDocumentPaginatorSource)copy).DocumentPaginator;

            return new PrintingPaginator(
                paginator,
                new Size(
                    pageWidth,
                    pageHeight),
                    new Size(
                        DPI,
                        DPI));



        }

        public void PrintPreview()
        {
            PrintPreviewDialog dlg = new PrintPreviewDialog(this);
            dlg.ShowDialog();
        }
    }
}

/// <summary>
/// This custom paginator wraps the original and adds some additional 
/// functionality. Notice that many of the properties pass through to
/// the orginal. The constructor begins by setting the original's page 
/// size to a size reflected by the requested size and margins and then
/// asks the underlying paginator to figure out how many pages we have.
/// 
/// The next function worth noting is the overide method GetPage. WPF 
/// calls this method each time it requires a page for display or print.  
/// </summary>
public class PrintingPaginator : DocumentPaginator 
{

    private readonly DocumentPaginator _originalPaginator;
    private readonly Size _pageSize;
    private readonly Size _pageMargin;

    public PrintingPaginator( DocumentPaginator paginator,
                              Size pageSize,
                              Size margin )
    {
        _originalPaginator = paginator;
        _pageSize = pageSize;
        _pageMargin = margin;


        _originalPaginator.PageSize = new Size(
            _pageSize.Width - _pageMargin.Width * 2,
            _pageSize.Height - _pageMargin.Height * 2);

        _originalPaginator.ComputePageCount();
    }


    public override bool IsPageCountValid
    {
        get
        {
            return _originalPaginator.IsPageCountValid;
        }
    }

    public override int PageCount
    {
        get
        {
            return _originalPaginator.PageCount;
        }
    }

    public override Size PageSize
    {
        get
        {
            return _originalPaginator.PageSize;
        }
        set
        {
            _originalPaginator.PageSize = value;
        }
    }

    public override IDocumentPaginatorSource Source
    {
        get
        {
            return _originalPaginator.Source;
        }
    }


    /// <summary>
    /// GetPage 
    /// 1. Obtain the original page form the original paginator
    /// 2. Wrap the page in a ContainerVisual, to make it easier manipulate.
    /// 3. Use a TranslateTransform to move the contents fo the ContainerVisual in line with the margins
    /// 4. Returns a new page object with its content and bleed adjusted in line with the margins
    /// </summary>
    /// <param name="pageNumber">
    /// Specifies the page number to be return by GetPage
    /// </param>
    /// <returns></returns>
    public override DocumentPage GetPage(int pageNumber)
    {
        DocumentPage originalPage = _originalPaginator.GetPage(pageNumber);
        ContainerVisual fixedPage = new ContainerVisual();

        fixedPage.Children.Add(originalPage.Visual);

        fixedPage.Transform = new TranslateTransform(
            _pageMargin.Width,
            _pageMargin.Height);

        return new DocumentPage(
                            fixedPage,
                            _pageSize,
                            AdjustForMargins(originalPage.BleedBox),
                            AdjustForMargins(originalPage.ContentBox)
                            );

    }

    private Rect AdjustForMargins(Rect rect)
    {
        if (rect.IsEmpty) return rect;
        else
        {
            return new Rect(
                            rect.Left + _pageMargin.Width,
                            rect.Top + _pageMargin.Height,
                            rect.Width,
                            rect.Height);
        }
    }



}