Speed Up Printing with the PrintPreviewDialog






4.14/5 (9 votes)
Supplies a custom PrintPreviewDialog that, at printing time, skips the passed printDocument object and prints from the previews.
Introduction
Have you ever had to do a printing system for your application and added all the computing logic into the printDocument
? Well, in that case, I think you were exactly at the point I was when I thought of this custom PrintPreviewDialog
.
So, what was the problem? Simple, the PrintPreviewDialog
generates previews of the document it receives, and shows them to the user. Nothing wrong with this. When the user presser the Print button, the dialog starts generating again and draws all the pages again. This is useful when you deal with a database that is prone to changes every second. But when you have the user satisfied with what he sees on the preview and the job is a hard one (with all the database reading logic in it - let's say, with 100 pages and 3 seconds for a page), this tends to be a headache.
The solution
The solution is to inherit the PrintPreviewDialog
, and make sure it prints from the previews generated previously. Sounds easy, but there is no other way to do that except using Reflection...
Behind the scenes
One of the main problems was to intercept the printButton
clicks from the toolbar that is on the top of the dialog. Adding another Click
handler to the toolbar does not help a bit because the old handler (from the base class) still does its job (prints from the PrintDocument
). My solution was to remove the button from the toolbar and add my PrintButton
that looked exactly like the previous one. Now, adding a new handle to the toolbar that checks if the button pressed was my new button was enough because the old button didn't have any chance of being clicked.
public SpeedUpPrintPreviewDialog() : base()
{
// we use reflection to be able to intercept
// the print button click on the dialog
// and do owe own fast printing
Type t = typeof(PrintPreviewDialog);
// first get the tool bar
FieldInfo fi = t.GetField("toolBar1",
BindingFlags.Instance |
BindingFlags.NonPublic);
// then get the button
FieldInfo fi2 = t.GetField("printButton",
BindingFlags.Instance |
BindingFlags.NonPublic);
// set ower class variables
ToolBar toolBar1 = (ToolBar)fi.GetValue(this);
ToolBarButton printButton =
(ToolBarButton)fi2.GetValue(this);
// hide the base printButton
printButton.Visible = false;
// create owr own prin button
myPrintButton = new ToolBarButton();
myPrintButton.ToolTipText = printButton.ToolTipText;
myPrintButton.ImageIndex = 0;
// recreate the base's toolbar buttons
// with ower own print button
ToolBarButton[] oldButtons =
new ToolBarButton[toolBar1.Buttons.Count-1];
for(int i = 0 ; i < oldButtons.Length ; i++)
oldButtons[i] = toolBar1.Buttons[i+1];
toolBar1.Buttons.Clear();
toolBar1.Buttons.Add(myPrintButton);
for(int i = 0 ; i < oldButtons.Length ; i++)
toolBar1.Buttons.Add(oldButtons[i]);
// add another click handle for the toolbar
toolBar1.ButtonClick +=
new ToolBarButtonClickEventHandler(toolBar1_Click);
// initialize the inner print document
innerPrintDocument = new PrintDocument();
innerPrintDocument.BeginPrint +=
new PrintEventHandler(innerPrintDocument_BeginPrint);
innerPrintDocument.PrintPage +=
new PrintPageEventHandler(innerPrintDocument_PrintPage);
}
The next step was to implement a custom prinDocument
object that printed from the previews generated by the dialog. Obtaining the previews from the dialog was kind of hard because they are private members, so Reflection was necessary once again...
private int NumberOfPages()
{
Type t = typeof(PrintPreviewControl);
FieldInfo pageInfo = t.GetField("pageInfo",
BindingFlags.Instance | BindingFlags.NonPublic);
PreviewPageInfo[] infos = (PreviewPageInfo[])
pageInfo.GetValue(this.PrintPreviewControl);
return infos.Length;
}
private Image GetImageForPage(int pageNumber)
{
Type t = typeof(PrintPreviewControl);
FieldInfo pageInfo = t.GetField("pageInfo",
BindingFlags.Instance | BindingFlags.NonPublic);
PreviewPageInfo[] infos = (PreviewPageInfo[])
pageInfo.GetValue(this.PrintPreviewControl);
return infos[pageNumber].Image;
}
Now, having the number of pages in the preview, the images from the preview, and having a handle that knows when the user wants to print, the next and final step was to implement my printDocument
to render each page from the preview.
private void innerPrintDocument_PrintPage(object sender, PrintPageEventArgs e)
{
// do owr drawing - print an image at the top of the page
e.Graphics.DrawImage(GetImageForPage(
innerPrintDocument_pageNumber), 0.0f, 0.0f);
// do we still have more pages?
if(innerPrintDocument_pageNumber == (NumberOfPages() - 1))
e.HasMorePages = false; // no? - finish
else
{
innerPrintDocument_pageNumber++; // yes? - go on
e.HasMorePages = true;
}
}
Using the code
The control is easy to use, handle it like the PrintPreviewDialog
. I hope somebody finds this of use, because it helped me.