May printing this FlowDocument be simplified?

Emon Haque 3,176 Reputation points
2020-10-06T12:28:17.717+00:00

I've an ICommand, PrintReport. Its execute function does this:

void printReport(object o)
{
    var dialog = new PrintDialog()
    {
        CurrentPageEnabled = true,
        UserPageRangeEnabled = true
    };

    if (dialog.ShowDialog() == true)
    {
        var size = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);               
        FlowPaginator.PageWidth = size.Width;
        FlowPaginator.PageHeight = size.Height;
        CreateReport(size);
        dialog.PrintDocument(new FlowPaginator(document) { PageSize = size }, string.Empty);
    }
}

FlowPaginator is a subclass of DocumentPaginator and I couldn't find any way of adding Header and Footer in my FlowDocument directly when I create that so I add those in this class,. First, I create the FlowDocument, document, and then pass that in the new FlowPaginator in PrintDocument method.

void CreateReport(Size pageSize)
{
    document = new FlowDocument()
    {
        PageWidth = pageSize.Width,
        PageHeight = pageSize.Height,
        PagePadding = new Thickness(FlowPaginator.Margin,
                                    Header(pageSize).DesiredSize.Height,
                                    FlowPaginator.Margin,
                                    FlowPaginator.Margin)
    };    
    var table = new Table()
    {
        FontFamily = SystemFonts.MessageFontFamily,
        FontStyle = SystemFonts.MessageFontStyle,
        FontSize = 11.0,
        CellSpacing = 0,
        Columns =
        {
            new TableColumn(){ Width = new GridLength(ColumnWidth) },
            new TableColumn(){ Width = new GridLength(pageSize.Width - (2.0 * FlowPaginator.Margin) - (4 * ColumnWidth)) },
            new TableColumn(){ Width = new GridLength(ColumnWidth) },
            new TableColumn(){ Width = new GridLength(ColumnWidth) },
            new TableColumn(){ Width = new GridLength(ColumnWidth) },
        }
    };
    var rowNum = 1;   
    foreach (var item in Reportables)
    {
        var rowGroup = new TableRowGroup();
        var row = new TableRow();
        row.Cells.Add(new TableCell(new Paragraph(new Run(item.Date.ToString("dd/MM/yyyy")))));
        row.Cells.Add(new TableCell(new Paragraph(new Run(item.Particulars))));
        row.Cells.Add(new TableCell(new Paragraph(new Run(item.Receivable.ToString("#,##0;(#,##0);-    ")))) { TextAlignment = TextAlignment.Right });
        row.Cells.Add(new TableCell(new Paragraph(new Run(item.Receipt.ToString("#,##0;(#,##0);-    ")))) { TextAlignment = TextAlignment.Right });
        row.Cells.Add(new TableCell(new Paragraph(new Run(item.Balance.ToString("#,##0;(#,##0);-    ")))) { TextAlignment = TextAlignment.Right });    
        if (rowNum % 2 == 0) row.Background = Brushes.LightBlue;    
        rowGroup.Rows.Add(row);
        table.RowGroups.Add(rowGroup);
        rowNum++;
    }
    document.Blocks.Add(table);
}

To have room for the Header on every page of the Flowdocument I call a function, Header(pageSize).DesiredSize.Height which returns an UIElement, in PagePadding. and for each column of my tabular report, I specify the TableColumn Width. The FlowPaginator class have these in it:

public class FlowPaginator : DocumentPaginator
{
    public static double PageWidth, PageHeight;
    public const double Margin = 96.0;
    DocumentPaginator paginator;    
    public FlowPaginator(FlowDocument document)
    {
        paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
    }
    public override bool IsPageCountValid => paginator.IsPageCountValid;
    public override int PageCount => paginator.PageCount;
    public override Size PageSize { get => paginator.PageSize; set => paginator.PageSize = value; }
    public override IDocumentPaginatorSource Source => paginator.Source;
    public override DocumentPage GetPage(int pageNumber)
    {
        return new DocumentPage(new ContainerVisual() 
        { 
            Children =
            {
                LedgerVM.Header(PageSize),
                paginator.GetPage(pageNumber).Visual,
                Footer(++pageNumber)
            }
        });
    }    
    UIElement Footer(int pageNo){ ... }
}

Here in the GetPage, I've to call the same static Header function, used in the CreateReport method, to add the header, then the padded content and finally have to call another function of the same class, Footer, to get footer on every page.

I wanted to use GridUnitType.Star for the second column of the FlowDocument and GridLength.Auto for the rest of the 4 columns BUT I haven't found any way of doing that so far and also wanted to add Header/Footer directly in the FlowDocument when I Create it, is there any way to do these or can this process be simplified further?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,710 questions
0 comments No comments
{count} votes

Accepted answer
  1. DaisyTian-1203 11,621 Reputation points
    2020-10-07T08:06:18.79+00:00

    How about adding header and footer in FlowPaginator like this:

    public class FlowPaginator: DocumentPaginator  
        {  
            DocumentPaginator m_paginator;  
      
            public DocumentPage GetPage(int pageNumber)  
            {  
                DocumentPage page = m_paginator.GetPage(pageNumber);  
                ContainerVisual newpage = new ContainerVisual();  
      
                //Header  
                DrawingVisual header = new DrawingVisual();  
                using (DrawingContext ctx = header.RenderOpen())  
                {  
                    //content for Header     
                }  
      
                DrawingVisual footer = new DrawingVisual();  
                using (DrawingContext ctx = footer.RenderOpen())  
                {  
                    //content for Footer  
                }  
      
                ContainerVisual mainPage = new ContainerVisual();  
                mainPage.Children.Add(page.Visual);  
                mainPage.Transform = new MatrixTransform(1, 0, 0, 0.95, 0, 0.025 * page.ContentBox.Height);  
      
                newpage.Children.Add(mainPage);  
                newpage.Children.Add(header);  
                newpage.Children.Add(footer);  
      
                return new DocumentPage(newpage, page.Size, page.BleedBox, page.ContentBox);  
            }  
        }  
    

    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful