Search code examples
c#wpfdocumentviewer

How to preview document with DocumentViewer with multi-pages per sheet in WPF


How can I preview a document with DocumentViewer with multi-pages per sheet (Like 2, 4, 6, 8, etc. pages per sheet) in WPF. In the default, DocumentViewer can only preview document with one page per sheet. But I want to preview document with multi-pages per sheet like this:

Google Chrome Print Dialog Pages Per Sheet Example

I just use the DocumentViewer control in WPF, I want to view document with multi-pages per sheet, but DocumentViewer doesn't have a property or method to do that. So I think I can change the DocumentViewer.Document property, like change the document, put multi-pages into one page, etc. But I don't know how to do that.

Thanks in advance.


Solution

  • I wrote a class to put a few FixedPage into one FixedPage, it works perfectly.

    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Markup;
    using System.Windows.Controls;
    using System.Windows.Documents;
    
    namespace PreviewHelper
    {
        internal enum DocumentOrientation
        {
            Portrait,
            Landscape
        }
    
        internal class MultiPagesPerSheetHelper
        {
            private readonly int _pagesPerSheet;
            private readonly Size _innerPageSize;
            private readonly FixedDocument _document;
            private readonly DocumentOrientation _documentOrientation;
    
            private readonly List<int> _allowedPagesPerSheetCount = new List<int> { 1, 2, 4, 6, 9, 16 };
            private readonly List<Size> _allowedPagesPerSheetArrange = new List<Size> { new Size(1, 1), new Size(1, 2), new Size(2, 2), new Size(2, 3), new Size(3, 3), new Size(4, 4) };
    
            public MultiPagesPerSheetHelper()
            {
                _document = null;
                _pagesPerSheet = 0;
                _innerPageSize = new Size();
                _documentOrientation = DocumentOrientation.Portrait;
            }
    
            public MultiPagesPerSheetHelper(int pagesPerSheet, FixedDocument document)
            {
                _document = document;
                _pagesPerSheet = pagesPerSheet;
                _innerPageSize = new Size();
                _documentOrientation = DocumentOrientation.Portrait;
            }
    
            public MultiPagesPerSheetHelper(int pagesPerSheet, FixedDocument document, Size innerPageSize)
            {
                _document = document;
                _pagesPerSheet = pagesPerSheet;
                _innerPageSize = innerPageSize;
                _documentOrientation = DocumentOrientation.Portrait;
            }
    
            public MultiPagesPerSheetHelper(int pagesPerSheet, FixedDocument document, Size innerPageSize, DocumentOrientation documentOrientation)
            {
                _document = document;
                _pagesPerSheet = pagesPerSheet;
                _innerPageSize = innerPageSize;
                _documentOrientation = documentOrientation;
            }
    
            public FixedDocument GetMultiPagesPerSheetDocument()
            {
                if (_document == null && _document.Pages.Count == 0 && _pagesPerSheet == 0)
                {
                    return new FixedDocument();
                }
                if (_allowedPagesPerSheetCount.Contains(_pagesPerSheet) == false)
                {
                    return new FixedDocument();
                }
    
                FixedDocument doc = new FixedDocument();
                doc.DocumentPaginator.PageSize = _document.DocumentPaginator.PageSize;
    
                int currentPageCount = 0;
                int pagesPerSheetIndex = _allowedPagesPerSheetCount.IndexOf(_pagesPerSheet);
                int arrangeRows;
                int arrangeColumns;
                if (_documentOrientation == DocumentOrientation.Portrait)
                {
                    arrangeRows = (int)_allowedPagesPerSheetArrange[pagesPerSheetIndex].Height;
                    arrangeColumns = (int)_allowedPagesPerSheetArrange[pagesPerSheetIndex].Width;
                }
                else
                {
                    arrangeRows = (int)_allowedPagesPerSheetArrange[pagesPerSheetIndex].Width;
                    arrangeColumns = (int)_allowedPagesPerSheetArrange[pagesPerSheetIndex].Height;
                }
                double innerPageHeight = doc.DocumentPaginator.PageSize.Height / arrangeRows;
                double innerPageWidth = doc.DocumentPaginator.PageSize.Width / arrangeColumns;
    
                while (currentPageCount < _document.Pages.Count)
                {
                    FixedPage outerPage = new FixedPage
                    {
                        Width = doc.DocumentPaginator.PageSize.Width,
                        Height = doc.DocumentPaginator.PageSize.Height
                    };
    
                    Grid outergrid = new Grid()
                    {
                        Width = outerPage.Width,
                        Height = outerPage.Height,
                    };
    
                    for (int i = 0; i < arrangeRows; i++)
                    {
                        outergrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
                    }
                    for (int i = 0; i < arrangeColumns; i++)
                    {
                        outergrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
                    }
    
                    for (int i = 0; i < arrangeRows; i++)
                    {
                        for (int j = 0; j < arrangeColumns && currentPageCount < _document.Pages.Count; j++)
                        {
                            Grid innerGrid = new Grid()
                            {
                                Width = innerPageWidth,
                                Height = innerPageHeight,
                                VerticalAlignment = VerticalAlignment.Center,
                                HorizontalAlignment = HorizontalAlignment.Center
                            };
                            innerGrid.SetValue(Grid.RowProperty, i);
                            innerGrid.SetValue(Grid.ColumnProperty, j);
    
                            string xaml = XamlWriter.Save(_document.Pages[currentPageCount].Child);
                            FixedPage innerPage = XamlReader.Parse(xaml) as FixedPage;
    
                            if (_innerPageSize.Width == 0 || _innerPageSize.Height == 0 || _innerPageSize.Width == double.NaN || _innerPageSize.Height == double.NaN)
                            {
                                innerPage.Width = outerPage.Width;
                                innerPage.Height = outerPage.Width;
                            }
                            else
                            {
                                innerPage.Width = _innerPageSize.Width;
                                innerPage.Height = _innerPageSize.Height;
                            }
    
                            innerPage.VerticalAlignment = VerticalAlignment.Center;
                            innerPage.HorizontalAlignment = HorizontalAlignment.Center;
    
                            double widthRatio;
                            double heightRatio;
    
                            widthRatio = innerPageWidth / innerPage.Width;
                            heightRatio = innerPageHeight / innerPage.Height;
    
                            if (innerPage.Height * widthRatio <= innerPageHeight)
                            {
                                innerPage.Width *= widthRatio;
                                innerPage.Height *= widthRatio;
    
                                innerPage.RenderTransform = new ScaleTransform(widthRatio, widthRatio);
                            }
                            else
                            {
                                innerPage.Width *= heightRatio;
                                innerPage.Height *= heightRatio;
    
                                innerPage.RenderTransform = new ScaleTransform(heightRatio, heightRatio);
                            }
    
                            innerGrid.Children.Add(innerPage);
                            outergrid.Children.Add(innerGrid);
    
                            currentPageCount++;
                        }
                    }
    
                    outerPage.Children.Add(outergrid);
    
                    doc.Pages.Add(new PageContent { Child = outerPage });
                }
    
                return doc;
            }
        }
    }
    

    And you can use it like this:

    PreviewHelper.MultiPagesPerSheetHelper multiPagesPerSheetHelper = new PreviewHelper.MultiPagesPerSheetHelper(PAGES_PER_SHEET_COUNT, ORGINAL_DOCUMENT, ORGINAL_DOCUMENT_SIZE, DOCUMENT_ORIENTATION);
    FixedDocument newDocument = multiPagesPerSheetHelper.GetMultiPagesPerSheetDocument();
    

    It will work like this:

    Print Dialog Pages Per Sheet