FormPrint as a simple class
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 landscape12/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 help01/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 enumeration03/22/2007
NET 2.0 improved to show RichTextBox or WebBrowser (DrawToBitmap is not available to those controls)