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:
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.
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);
this.m_nCurrentPage = 0;
this.m_oPages.Clear();
if (this.DocBuilder != null) {
this.DocBuilder.Build(this);
}
for(int nPage = 0; nPage < this.PageCount; nPage++) {
Page oPage = this.GetPage(nPage);
oPage.Skip = true;
if ( this.m_bEvenPageOnly ) {
oPage.Skip = ((nPage % 2) == 0) ? true : false;
}
else if ( this.m_bOddPageOnly ) {
oPage.Skip = ((nPage % 2) == 0) ? false : true;
}
else {
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);
Graphics gdiPage = e.Graphics;
float leftMargin = e.MarginBounds.Left;
float topMargin = e.MarginBounds.Top;
Rectangle oArea = e.MarginBounds;
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 );
}
e.HasMorePages = this.HasMorePages();
}
catch {
e.HasMorePages = false;
}
}
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();
oDocument.OddPageOnly = true;
oPrintDlg.Parent = this.Handle;
oPrintDlg.Document = oDocument;
oPrintDlg.UseEXDialog = true;
DialogResult nResult = oPrintDlg.ShowDialog();
Point of Interests
- Represent any source print data in an XML form
References
- Printing Overview, MSDN Documentation
- Extend the Common Dialog Boxes, MSDN Tutorials
History
- 2006.12.18 - Original version
A graduate in Bachelor of Science in Computer Engineering from the Philippines. His love for software programming/design/patterns started in his college days when he found out that he possesses above average skill in computer programming. He started his career as a computer instructor for a year, then shifts to work as a software design engineer in one of the renowned Japanese company in the Philippines. After almost 5 years working in that company he got an opportunity to work in japan were he now currently lives and work since 2003.