65.9K
CodeProject is changing. Read more.
Home

Extending C# PrintDialog Part II

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.07/5 (7 votes)

Dec 19, 2006

CPOL

3 min read

viewsIcon

78140

downloadIcon

2740

Printing Odd/Even pages in C#

Printing Odd/Even Pages

Introduction

C# provides a standard PrintDialog, which has all the basic print dialog functionality. Since it is declared as a sealed class, there is no way to extend it through C# native syntax. This article provides an alternate means to extend the functionality of the C# PrintDialog.

There are a lot of additional features that can be added to the common print dialog; one's imagination is the only limit. In this article, I will present a print dialog with an extended panel having: even/odd print page combobox, page setting button and an image.

Overview

PrintDialogEx serves as a wrapper to systems PrintDlg(common dialog), which uses P/Invoke methods to access the Win32 system's low-level APIs. Hook the dialog window mainproc and attach our extended user control panel when it receives a WM_INIT_DIALOG event notification. For a more detailed explanation, please refer to my previous article Extending C# PrintDialog Part I.

The main logic in achieving printing of odd/even pages is a general printing framework which, abstracts each print page into a C# class having common properties for a print page; included in these properties is a flag which indicates if the page should be printed or skipped during printing. The below figure depicts this concept:

Block Diagram

Generic Printing Framework

The framework was created with a general printing capability in mind; which means printing any sort of data namely: texts, images, tables having variable formatting/sizes and can accept any type of source data. For this article's purpose, only text printing is currently supported. However, the framework can be used as a base skeleton for user customizations and improvements.

Class Diagram

PrintDocumentEx is a customized print document object derived from System.Drawing.PrintDocument. It overrides and handles the following events: OnBeginPrint, OnEndPrint, OnPrintPage. These events are automatically called by the system. For a more detailed information, please refer to: Using PrintDocument.

OnBeginPrint - is called after the Print method is called and before the first page of the document prints. This is where we inject our code to create each PrintSection, layouts and calculate its size and add it to the next available PrintPage from PrintDocumentEx list container. Using a well-known design pattern called: Builder by Gamma, Helm, Johnson and Vlissides; to create different print document layout and presentations.

protected override void OnBeginPrint(PrintEventArgs e) {
    base.OnBeginPrint(e);

    // reset current page index
    this.m_nCurrentPage = 0;

    // remove all previous print pages.
    this.m_oPages.Clear();
    if (this.DocBuilder != null) {
        // create the print document page layouts.
        this.DocBuilder.Build(this);
    }

    // initialize page skip flag attribute
    for(int nPage = 0; nPage < this.PageCount; nPage++) {
        Page oPage = this.GetPage(nPage);
        oPage.Skip = true;
        if ( this.m_bEvenPageOnly ) {
            // set page skip flag to false if page num is even
            oPage.Skip = ((nPage % 2) == 0) ? true : false;
        }
        else if ( this.m_bOddPageOnly  ) {
            // set page skip flag to false if page num is odd
            oPage.Skip = ((nPage % 2) == 0) ? false : true;
        }
        else {
            // prints all page
            oPage.Skip = false;
        }
    }
}

The document builder creates the appropriate concrete section object based on the print data type. After all the print pages initialization, we iterate all print pages and set its skipped flag value to true or false, depending on our PrintDocumentEx OddPageOnly or EvenPageOnly property value.

OnPrintPage - is called before a page prints. Here we iterate through our print page container list and manually draw each section contained on each page to a GDI graphics object(PrintPageEventArgs.Graphics).

protected override void OnPrintPage(PrintPageEventArgs e) 
{
    try {
        base.OnPrintPage(e);

        // get the page print area
        Graphics gdiPage = e.Graphics;
        float leftMargin = e.MarginBounds.Left;
        float topMargin = e.MarginBounds.Top;
        Rectangle oArea = e.MarginBounds;

        // get the next page, labelled for printing.
        Page oPage = this.GetPage(this.m_nCurrentPage++);
        while( oPage.Skip && (this.m_nCurrentPage < this.PageCount) ) {
            oPage = this.GetPage(this.m_nCurrentPage++);
        }

        if ( (null != oPage) && !oPage.Skip ) {
            gdiPage.DrawString("Page " + this.m_nCurrentPage, 
			new Font("Verdana", 10), Brushes.Black, 
                leftMargin, 0 );

            oPage.OnPrintPage(gdiPage, this.m_oPrintArea );
        }

        // checks if there are more pages to print
        e.HasMorePages = this.HasMorePages();
    }
    catch { // handle all unknown errors during printing.
        e.HasMorePages = false;
    }

} // OnPrintPage

OnEndPrint - is called when the last page of the document has printed. nothing fancy here, we just removed all pages contained in our list.

Using the Code

Client application needs only to create a PrintDialogEx, associate it to an instance of PrintDocumentEx before calling its ShowDialog(). By default, we can set to print odd/even pages by setting the PrintDocumentEx's OddPageOnly or EvenPageOnly to true. Below is a sample code snippet to do this:

PrintDialogEx oPrintDlg = new PrintDialogEx();

PrintDocumentEx oDocument = PrintDocumentEx();
// set to print odd pages only.
oDocument.OddPageOnly = true;

// set the dialogs owner window(optional)
// parent handle should be a handle to a window Form
oPrintDlg.Parent = this.Handle;
// associate this dialog to a print document instance.
oPrintDlg.Document = oDocument;

// Shows the print dialog extended control panel by default.
oPrintDlg.UseEXDialog = true;
// display the print dialog with extended panel control
DialogResult nResult = oPrintDlg.ShowDialog();

Point of Interests

  • Represent any source print data in an XML form

References

  1. Printing Overview, MSDN Documentation
  2. Extend the Common Dialog Boxes, MSDN Tutorials

History

  • 2006.12.18 - Original version