Click here to Skip to main content
Click here to Skip to main content

FormPrint as a simple class

, 27 Mar 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
An article on how to print or save (as a bitmap file) WinForms or any other controls.

Introduction

The "private class" FormPrint enables you to hardcopy any form or other control to a printer or to an image file. To do so, the class uses the public static methods Print() or Save(). This might be useful for documentation and debugging reasons, either for developers or for users.

Other solutions I found in discussion boards were too large and complex to be used simply. Therefore, I decided to create my own solution.

Using the code

The code is written for NET 2.0. An Adaptation to NET 1.1 is described later on in this article.

Insert into your source code a reference to this class:

using JThomas.Tools;

If you put this class into a separate assembly named Tools, you need a reference in your application to Tools.Dll, of course.

Insert into your source code, e.g., in a Click event for a button or a ToolStripMenuItem, the call of the class' methods with the form or control specified.

Versions to print

Here are some examples to print:

/// print the actual form on the default printer with default settings
FormPrint.Print(this);

/// print another form that may not call a null reference, of course
FormPrint.Print(WinForm2);

/// print any control on the default printer with default settings, e.g.:
FormPrint.Print(panel1);

/// print using special settings: 8 bpp pixelformat, another printer, 
/// your own margins instead of the default ones, landscape orientation
Margins aMargins = new System.Drawing.Printing.Margins(80,50,40,40);
FormPrint.Print(this, ColorDepthReduction.Colored8bpp, 
  "PDF Printer", aMargins, false);

Versions to save

Here are some examples to save:

/// Save: this version sets filename and uses png format
FormPrint.Save(this, "E:\\Temp\\MainForm.Png");
/// Save: this version sets ImageFormat and filename with standard folder
FormPrint.Save(listBox1, System.Drawing.Imaging
  .ImageFormat.Bmp, "Listbox.Bmp");

Most of the parameters can be omitted.

Parameters for all versions

Control Ctrl means the form, or any other control that is to be printed or saved. This parameter is mandatory.

ImageFormat fImage tells how the bitmap has to be saved; if omitted, ImageFormat.Png is used. Printing always uses png format.

Parameters for printing

ColorDepthReduction f8bitPixel explains how the bitmap is printed: Standard is None, i.e. PixelFormat.Canonical as 32 bit colors; Colored8bpp converts the bitmap to PixelFormat.Format8bppIndexed, and Grayscaled8bpp to 8 bpp grayscales. Take a look at "Printer problems" later.

string sPrinterName explains that another printer than the standard printer is to be used. If omitted, the standard printer is used.

Margins aMargins sets special page margins. If omitted, the default margins of the current printer are used.

bool bPortrait sets orientation as portrait (true) or landscape (false). If omitted, orientation is set to portrait.

Parameter for saving

string sFileName contains the complete filename. If it's only a filename without path, the bitmap is saved in the actual folder. If the filename is omitted, a standard filename is created as: user's folder ‚ MyPictures', control's name, and extension by imageformat.

How it works

The FormPrint class creates a bitmap for the required form or control. This is sent to the standard printer or to a file in a suitable format. One can call the actual form or another form in the application, but also any other control (e.g., GroupBox, Panel, ...). All that is combined in a simple class FormPrint.

The FormPrint class contains the following private variables:

//  The FormPrint instance cls is used to execute printing resp. saving.
static FormPrint cls;
//  The bitmap bmp contains the copy of the control's graphics.
Bitmap bmp;
//  deciding whether to print or save
bool Printing;
//  additional: variables related to each of the parameters 
//  mentioned above

You call one of the overloaded static methods FormPrint.Print() and FormPrint.Save() using the required parameters. If you omit a parameter, default values are used.

All variants call a private static method FormPrint.Start() that provides the work: First, it checks if all required parameters are set. Next, a private instance cls of the FormPrint class is created passing all parameters. Finally, the private methods StartPrinting() and StartSaving() do the underlying work.

//  create the private formprint class cls
cls = new FormPrint(Ctrl, bPrint, fImage, f8bitPixel, 
        sPrinterName, aMargins,  bPortrait, sFileName);
//  execute the required printing resp. saving method
try {
    if (bPrint) {
        cls.StartPrinting();
    } else {
        cls.StartSaving();
    }        
} finally {
    //  free resources
    cls.bmp.Dispose();
    cls.bmp = null;
    //  in some situations, the next command is useful
    Ctrl.Refresh();
}

The constructor creates a Bitmap object bmp with the size and graphics of the control and copies the screen content of the control into the Bitmap object bmp.

private FormPrint(Control Ctrl, bool bPrint,
    //      formatings, convertings
    ImageFormat fImage, ColorDepthReduction f8bitPixel
    //  additional parameters for printing
    string sPrinterName, Margins aMargins, bool bPortrait,
    //  additional parameter for saving
    string sFileName)
{
    //  call the bitmap in the propriate way - NET 1.1 or NET 2.0
    ctrl = Ctrl;
    bmp = GetBitmap(Ctrl);

    /*  set all other parameters ... these instructions are removed here  */

}

Under NET 2.0, one can use a single instruction to copy the control's image.

//  NET 2.0 - Copy the control's graphics directly to the bitmap
private Bitmap GetBitmap(Control Ctrl) {
    //  prepare the bitmap
    Graphics grCtrl = Ctrl.CreateGraphics();
    Bitmap Result = new Bitmap(Ctrl.Width, Ctrl.Height, 
    grCtrl);

    //  copy the control's graphics – not available 
    with NET 1.1
    Ctrl.DrawToBitmap( Result, new Rectangle(0, 0, 
    Ctrl.Width, Ctrl.Height));

    //  that's all work for the bmp using NET 2.0 
    //  instructions
    return Result;
}

Under NET 1.1, one has to use a complex workaround using Windows GDI. Therefore, this work is done by a separate method GetBitmap(). Take a look at the source code, if you want more information about that.

If you want to print, bmp is sent to a PrintDocument. If required, some instructions are used (but not shown in the following snippet): to apply printer name or page settings, compressing a large bitmap to paper size, converting a 32bpp bitmap to 8bpp bitmap to avoid printer memory problems.

private void StartPrinting() {
    //  create a document; the event handler PrintPage
    //  will connect it with the bitmap
    using(PrintDocument doc = new PrintDocument()){
        doc.PrintPage += new PrintPageEventHandler(
            doc_PrintPage);

        //  setting printer name and page settings, 
        //  if required

        //  if margins are set, then check if bitmap has to
        //  be compressed
        if (PrinterMargins != null) {
            //  check if bmp size is larger than PaperSize
            if ( (bmp.Width > bmpX) || (bmp.Height > 
                  bmpY) ) 
                GetThumbnail((int)bmpX, (int)bmpY);
        }
        //  convert to 8-bit-pixel or grayscale to avoid 
        //  printer problems
        if (format8bitPixel != ColorDepthReduction.None) {
            ConvertBitmapTo8Bpp(15);

        //  now print
        doc.DocumentName = SaveFileName;
        doc.Print();
    }
}

If you want to save, it's directly done by:

bmp.Save(SaveFileName, formatImage);

Additionally, the code contains some try-catch blocks, the PrintPageEventHandler to connect the bmp to the PrintDocument, doc, freeing resources, and the utilities GetThumbnail() to compress the bitmap and ConvertBitmapTo8Bpp().

Because converting can take some seconds, a ProgressForm with a simple label is included. This is shown only if the printed bitmap has more than 40,000 pixels (e.g., 200x200).

Additional hints for using

If you call the method via MenuItem.Click event, you should use a ShortCut; otherwise the opened menu is copied, too. Pay attention: Always the actual screen content is printed or saved; if the form is partially hidden, the foreground screen is printed. Controls that do not match are not always printed correctly, e.g., a normal button on a toolbar. If Control.AutoScroll is set, one must pay attention at the SDK documentation.

Adaptation to NET 1.1

Two instructions are not compatible with NET 1.1; one of them needs a complex way to workaround. To adapt, set the appropriate #define directive:

/// with NET 2.0 use as follows:
#define NET_V20
#undef  NET_V11
/// with NET 1.1 use as follows:
#define NET_V11
#undef  NET_V20

Printer problems

In both versions, a full screen form could lead to printer problems, like "memory full" or "too many data". If so, try to use 8 bit pixel format by ColorDepthReduction f8bitPixel parameter. If the problem remains, you should print a smaller control, e.g. a panel or groupbox, rather than the whole form. In that case, I recommend to combine your form with some settings which control should be printed.

Demo formular

To create a demo application, use the FormPrintDemoForm.cs as main form and FormPrint.cs as additional class. The form contains a listbox to choose any variant of FormPrint.Print() and FormPrint.Save() and a panel with 256 colored labels. You have to change the constant values in the buttonStart_Click() method and the richTextBox1.LoadFile() call in the constructor.

Possible extensions

I chose more parameters than I originally planned. I omit the following possibilities:

  • Page Setup dialog
  • Message box, if the file is saved correctly

Thanks for help

Initially, I chose the way in the MSDN Library How to: Print a Windows Form.

Tim van Wassenhove told me to use DrawToBitmap().

"Programmierhans" showed how to copy a control's graphics with NET 1.1, and in his post at Aug 2, 2005 19:49 the method GetThumbnail().

Robert Schmitz "robertico" developed converting a bitmap quickly to an 8bpp pixel format and grayscaling via ConvertBitmapTo8Bpp(). (MSDN solution with GetPixel/SetPixel needs an unacceptable amount of time.)

My intention is to combine all that to a simple class extended by variable parameters.

History

  • 12/17/2006 - First version.

  • 12/19/2006
    1. added: clearing resources (Bitmap, Graphics)
    2. added: printing as portrait or landscape

  • 12/24/2006
    1. changed: a shorter way to copy the control's graphics into the bitmap's one
    2. comment changed: at present, there is no adaptation to NET 1.1.

  • 12/25/2006
    1. added: copy the control's graphics to the bitmap with NET 1.1
    2. changed: variables and methods are grouped newly, but with no other features
    3. comment changed: adaptation to NET 1.1
    4. comment added: known problems, thanks for help

  • 01/13/2007
    1. NET 1.1 changed: form can be printed including title and menu bar
    2. changed: public method Execute() splitted to Print() resp. Save()
    3. changed: Print() allows several parameters: PrinterName, Margins, Orientation
    4. changed: printing can compress to papersize with margins if necessary
    5. changed: most of the parameters can be omitted
    6. changed: printer memory problem managed by converting to PixelFormat.Format8bppIndexed and GrayScaled bitmap instead of colored PixelFormat.Canonical
    7. changed: Execute() uses progress label while printing a large control (more than 40000 pixels, e.g. 200x200)
    8. changed: private method Execute() splitted to StartPrinting() resp. StartSaving()

  • 01/22/2007
    1. changed: adaptation to NET 1.1 via #define
    2. changed: PixelFormat.Format8bppIndexed and grayscaling replaced by ColorDepthReduction enumeration

  • 03/22/2007
    NET 2.0 improved to show RichTextBox or WebBrowser (DrawToBitmap is not available to those controls)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

J.Thomas

Germany Germany
No Biography provided

Comments and Discussions

 
GeneralDrawToBitmap is not perfect PinmemberDominique Bijnens5-Jun-08 21:59 
GeneralRe: Indeed - DrawToBitmap is not perfect PinmemberJ.Thomas6-Jun-08 7:09 
QuestionHelp!! PinmemberBaba Deep18-Oct-07 17:48 
AnswerRe: Help!! PinmemberJ.Thomas18-Oct-07 21:03 
QuestionIs there any function about PrintPreview? Pinmemberplayhere1-Sep-07 0:30 
AnswerRe: Is there any function about PrintPreview? PinmemberJ.Thomas1-Sep-07 1:48 
GeneralThanks! PinmemberSolburn22-Mar-07 3:45 
GeneralJ.Thomas you are you brilliant PinmemberSacha Barber16-Feb-07 3:36 
GeneralRe: brilliant - don't overdo PinmemberJ.Thomas16-Feb-07 5:11 
GeneralRe: brilliant - don't overdo PinmemberSacha Barber16-Feb-07 6:19 
GeneralRe: brilliant - don't overdo PinmemberJ.Thomas16-Feb-07 6:27 
GeneralRe: brilliant - don't overdo PinmemberSacha Barber16-Feb-07 8:05 
GeneralTrying to make it better - NET 1.1 is coming [modified] PinmemberJ.Thomas24-Dec-06 1:05 
GeneralRe: More complete solution [modified] Pinmembertimvw31-Dec-06 2:43 
GeneralRe: More complete solution PinmemberSacha Barber16-Feb-07 4:31 
GeneralRe: More complete solution (AGAIN) PinmemberSacha Barber16-Feb-07 4:52 
GeneralIt won't work with .NET 1.1 Pinmembertrackmz2k23-Dec-06 3:02 
QuestionWhat about using the Control.DrawToBitmap method? Pinmembertimvw22-Dec-06 9:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141022.2 | Last Updated 27 Mar 2007
Article Copyright 2006 by J.Thomas
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid