Skip to content
Michael Mairegger edited this page Nov 26, 2015 · 5 revisions

Documentation under development and review

Introduction

For an initial overview please refer to the first version of this library WPF Printing Library v1.0

What can be done until now using this library?

  1. Printing footer/header/recipient on every single page
    • Decide whether the footer is print only on the last page
    • Decide whether the header is print on every page, but not on the last page
    • ...
  2. Printing the page count on every page
  3. Decide what has to be printed: e.g. header and no footer, no page count
  4. Print a new item on a new page if there is not enough page available
  5. Printing a customizable summary of all items at the end of the report

Resolved drawbacks from v1.0

  1. If an item is longer than the available space for printing items you will now not get an infinite loop.
  2. According standards of WPF, that a control is created only in the main thread, you cannot print in background
  3. It is now possible to print to OneNote or XPS.
  4. It is now possible to decide whether e.g the footer is printed only on the last page.

Class Library

##PrintAppendixes

The PrintAppendixes is a flag-enum that defines the parts that have to be printed. Here you can set up if you want to print only the page footer, page header, footer and header or all other possible combinations of all values. As you certainly know, flag enums can be combined. That means you can select only one value but also more of them by applying it with the binary operator |.

PrintAppendixes pa = PrintAppendixes.Header | PrintAppendixes.Footer;

This enum is used for the PrintOnPage attribute that has to be added on the PrintProcessor class.

###IPrintPartDefinition I have decided to remove the definition of the print parts that have to be printed from the PrintDimension constructor. The reason for that was, because there you could set the PrintAppendixes only once and this will hold for the whole document. I thought that it would be useful to define for every page whether the PrintAppendix should be print or not. Therefore I created an Attribute hierarchy that enables this. It is pretty cool and simple. Just set an attribute of your choice on your implementation of the PrintProcessor class. You have four types of attributes available:

  • PrintOnPage
  • PrintOnAllPages
  • ExcludeFromPage
  • ExcludeFromAllPages

I think the names are self-explanatory.

Example: You want to print the footer only on the last page, but the header on every page except the last one. You achieve this by defining such a PrintProcessor class:

[PrintOnAllPages(PrintAppendixes.Header)]
[ExcludeFromPage(PrintAppendixes. Header, PrintPartDefinition.LastPage)]
[PrintOnPage(PrintAppendixes.Footer, PrintPartDefinition.LastPage)]
public abstract class TestPrintProcessor : PrintProcessor {}

That is all you need to do. Cool isn't it? But what is happening if I define following attributes:

[PrintOnAllPages(PrintAppendixes.Footer)]
[ExcludeFromPage(PrintAppendixes.Footer,1,4,5)]

If you write such a print part definition, that the footer gets only printed on page 2, 3, 6… If for a single page the ExcludeFrom and PrintOn is defined, than this print part will not be printed on this page.Now you might think that this is pretty cool, but it is very static and needs to be defined during coding. That's right for the attribute aspect. But I have created an additional property PrintDefinition in the PrintProcessor class on which you can call the SetPrintAttribute(IPrintPartDefinition definition) method. Now you can change when to print which print part at any time.

PrintProcessor

The PrintProcessor class is the class of mayor interest. It is an abstract class based on the interface IPrintProcessor and provides all necessary methods and properties for creating a cool print with custom headers, footers etc. In order to do that you should override the corresponding methods (GetFooter, GetHeader,…).

Note: If you define a header on page 2 and you do not provide a value for GetHeader() an exception gets raised. Not providing a header means that you do either return null or that you have not overwritten the method GetHeader().

Alternating the rows

An additional feature is to color the rows as you desire. To do that you can set a list of Brushes to the AlternatingRowColors property. But this is not enough: you have to enable the alternating coloring through the property IsAlternatingRowColor. If you just set IsAlternatingRowColor to true but do not set a value to AlternatingRowColors the default values, transparent and light gray, were used.

Break last item on new page

As you continue in printing it might be sometimes the case that you want to print the footer only on the last page. So it can be that all elements have been printed, but the footer does not fit on the last page. So we need a page break only to fit the footer on the last page. So we have the situation that on the last page the footer (and other PrintAppendixes) is printed. In order to avoid such an awkward situation you can set the property BreakLastItemIfLastPageWouldBeEmpty to.

Print result is not what you wanted

A very cool feature, for debugging reasons only is that you can enable coloring the print parts by setting ColorPrintPartsForDebug to true. This will color all the PrintAppendixes in such a way that you can see where space for a too large element is wasted. Demo ColorPrintPartsForDebug

Bulk print

Beginning from version 2.0 you can bulk print. This method takes a list of PrintProcessors that gets printed all at once.

Print direct to printer

Also a new feature for this version is that you can start printing and choose the printer from the print dialog, but also by direct printing to the printer. This is achieved by calling one overload of the Print method. You have two overloads available:

  • PrintDocument(printQueueName)
  • PrintDocument(printQueueName, PrintServer)
printer.PrintDocument("PDF-XChange Printer 2012");

The second overload overload is used to print to a LocalPrintServer and set some settings to it.

PrintDimension

The next class that is of interest is the PrintDimension class. It provides all necessary information to paginate the document.

Here you can control the height that is reserved for each print part if it gets printed on the page. In addition I suggest to add some properties for the column width for the IPrintContent.Content panel. This is very useful because in many times this content panel is in tabular form. The default contstructor initializes the Margin to {0,0,0,0}

Relative column position

Facts that your page size may vary from print to print you want stretch or shrink the print. In version 1.0 you set for every column in the IPrintContent.Content panel the width for each column. By setting UseRelativeColumnPosition to true you enable that the columns gets shrink a stretched as they have space. To define the width for a column you simply derive from PrintDimension and create some properties with a ColumnDimensionAttribute on it. The constructor takes a double representing the percentage that a column may fill up. As second parameter you can passeither ColumnDimensionType.Absolute or ColumnDimensionType.Relative relative. These two values are for defining whether the double value is an absolute width value for the column or like in the exaple below 50% of the page width.

Example: ColumnWidthAttribute

If you want to achieve columns that has exactly that width, than you write:

public class CustomPrintDimension: PrintDimension
{
    public CustomPrintDimension()
    {
        UseRelativeColumnPosition = true;
    }

    [ColumnDimension(.50,ColumnDimensionType.Relative)]
    public double WidthColumn1 { get; set; }

    [ColumnDimension(.15,ColumnDimensionType.Relative)]
    public double WidthColumn2 { get; set; }

    [ColumnDimension(.35,ColumnDimensionType.Relative)]
    public double WidthColumn3 { get; set; }
}

//usage

public override UIElement GetTable(out double tableHeaderHeight, out Brush borderBrush)
{
    Grid g = new Grid();
    borderBrush = Brushes.Gray;
    // for _printDimension.WidthColumn1 you will a value that repesents 50%
    // of the width of the page according the page orientation an page size.
    g.ColumnDefinitions.Add(new ColumnDefinition
	    { Width = new GridLength(_printDimensions.WidthColumn1) });
    g.ColumnDefinitions.Add(new ColumnDefinition
        { Width = new GridLength(_printDimensions.WidthColumn2) });
    g.ColumnDefinitions.Add(new ColumnDefinition
        { Width = new GridLength(_printDimensions.WidthColumn3) });
}

Note: If you change the PageOrientation in your PrintProcessor class from Portrait, which is the default value, to Landscape you will get bigger values for WidthColumn1,WidthColumn2 and WidthColumn3 because the width of the printable area changed.

IPrintContent

This interface was refactored from ILineItem. It provides a single property only. With that property you can provide the content of the line. Unlike in the previous version you do not have to provide the height of the line. This gets computed automatically.

NOTE: If the content is bigger than the remaining space of a page the element gets positioned automatically on a new page no matter if it has space or not.

public class TestPrintContent : IPrintContent
{
    private readonly int _item;
    public TestPrintContent(int item)
    {
        _item = item;
    }
    public Panel Content
    {
        get
        {
            var sp = new Grid();
            var textBlock = new TextBlock
                {Text = _item.ToString(),};
            sp.Children.Add(textBlock);
            return sp;
        }
    }
}

If you want to specify exactly the height of the content you can do this as follows:

public Panel Content
{
    get
    {
        var sp = new Grid();
        sp.Backgroud = Brushes.Red; //only to make the grid visible.
        sp.Height = 150;
        return sp;
    }
}

PrintProcessorBackground

As you might have seen in the PrintProcessor class there is a GetBackgroud() method that provides the background of the document. The class provides only methods for positioning the background on the document and the background element, of that the opacity can be set.

Sample C# project

You can download a complete working "Visual Studio 2012 project" that demonstrates my printing library by using this link: Click here to download the sample project.

NOTE: please change the file format of the downloading document from .docx to .zip.

Furure Work

  1. Compatibility with UWP
  2. ...