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:
FormPrint.Print(this);
FormPrint.Print(WinForm2);
FormPrint.Print(panel1);
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:
FormPrint.Save(this, "E:\\Temp\\MainForm.Png");
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:
static FormPrint cls;
Bitmap bmp;
bool Printing;
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.
cls = new FormPrint(Ctrl, bPrint, fImage, f8bitPixel,
sPrinterName, aMargins, bPortrait, sFileName);
try {
if (bPrint) {
cls.StartPrinting();
} else {
cls.StartSaving();
}
} finally {
cls.bmp.Dispose();
cls.bmp = null;
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,
ImageFormat fImage, ColorDepthReduction f8bitPixel
string sPrinterName, Margins aMargins, bool bPortrait,
string sFileName)
{
ctrl = Ctrl;
bmp = GetBitmap(Ctrl);
}
Under NET 2.0, one can use a single instruction to copy the
control's image.
private Bitmap GetBitmap(Control Ctrl) {
Graphics grCtrl = Ctrl.CreateGraphics();
Bitmap Result = new Bitmap(Ctrl.Width, Ctrl.Height,
grCtrl);
with NET 1.1
Ctrl.DrawToBitmap( Result, new Rectangle(0, 0,
Ctrl.Width, Ctrl.Height));
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() {
using(PrintDocument doc = new PrintDocument()){
doc.PrintPage += new PrintPageEventHandler(
doc_PrintPage);
if (PrinterMargins != null) {
if ( (bmp.Width > bmpX) || (bmp.Height >
bmpY) )
GetThumbnail((int)bmpX, (int)bmpY);
}
if (format8bitPixel != ColorDepthReduction.None) {
ConvertBitmapTo8Bpp(15);
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:
#define NET_V20
#undef NET_V11
#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)