Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Speed Up Printing with the PrintPreviewDialog

0.00/5 (No votes)
30 Apr 2006 1  
Supplies a custom PrintPreviewDialog that, at printing time, skips the passed printDocument object and prints from the previews.

Sample Image - image.jpg

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here