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

FormPrint as a simple class

0.00/5 (No votes)
27 Mar 2007 2  
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 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