Introduction
"The PrintForm Component is designed to bring back the ability to easily print a Windows Form."
This article begins with the basic steps to use the PrintForm
component and simple Form printing, describes various options for customised printing with code examples where necessary, and addresses some problems faced with the usage.
Background
Sometime back, I was working on a project in VB.NET, which required printing reports. It was a very lengthy job as back then, no such component was available. This was a big drawback with working in VB.NET as compared to VB. But now, the release of the PrintForm
component has proven a boon for both, VB.NET developers and those who are trying to upgrade their projects from VB to VB.NET, as you now no longer need to generate long sequences of GDI calls. You can simply print your Form, as desired, with minimal code. However, despite the ease and simplicity of the usage of the PrintForm
component, problems and doubts about this have been posted on forums of almost all developer sites and hardly any examples given. This is what made me decide to write about this component.
How to start
First things first. You need to download and install the PrintForm
component if you have not already done that. Here is a link to the download http://msdn2.microsoft.com/en-us/vbasic/aa701261.aspx
Installation is very simple, generally no problems occur during that. After installation, you will need to add it to your toolbox so that you can then drop it onto your Form:
- In the Visual Studio .NET editor, right click on the 'General' tab of the Toolbox, and select 'Customize Toolbox...'
or add it to the Windows Forms tab
or create a new tab. - Select the .NET Framework Components tab of the Customize Toolbox Dialog and scroll down until you find '
PrintForm
'. - Check it and click OK.
Now you can drag a PrintForm
component from the General Tab of the Toolbox onto the Form's Component tray. PrintForm
extends each control on your Form, and the Form itself, with new properties, which can be set according to requirements.
Printing the Form
To print the entire form, perform the following steps:
- Drag a
PrintForm
component onto your Form from the Visual Studio Toolbox - Set the
PrintForm.BodyContainer
property to reference your Form - Add a button and button click handler. In the handler call
PrintForm.Print()
Print Previewing the Form
Often you need to print preview the form before actual printing, especially during testing, so that you can adjust the properties accordingly, and of course not to forget, to save paper (and hence trees)
It can be done as follows:
- Drag a
PrintForm
component onto the Form from the Visual Studio Toolbox - Set the
PrintForm.BodyContainer
property to reference your Form - Drag a
PrintPreviewDialog
component onto your Form from the Visual Studio Toolbox - Set the
PrintPreviewDialog.Document
property to reference the PrintForm
component - Add a button and button click handler. In the handler call
PrintPreviewDialog.ShowDialog()
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Button1.Visible = False
PrintForm1.PrintAction = Printing.PrintAction.PrintToPreview
PrintForm1.Print()
Button1.Visible = True
End Sub
Customizing the Form Printout
In order to change the Background color of the Form on the printout, you can set BackColorWhilePrinting
of the Form. The background color of all the controls on the form will get changed. However, if you want to specify different colors for different forms, you can set this property for the individual controls.
If you don't want specific controls on the Form to get printed(for instance, command buttons), you can set VisibleWhitePrinting
property for those controls on the Form.
For centering the printed form on the page, in the horizontal or vertical dimension, or both, you can use the PrintForm.CenterStyle
property.
When the Form or control you are printing is smaller than the Margins of the printed page, PrintForm
uses the CentreStyle
setting to center the output, alternatively to print the output at the top left of the Margin rectangle set
CenterStyle = CenterStyle.None
.
To zoom the form to fill the printed page, setting the AutoFit
property to one of these values controls this behavior: PageElement.None
, PageElement.Body
, PageElement.All
.
To add headers and footers to the printout, there are three properties that can be set to point at individual controls: HeaderContainer
, BodyContainer
and FooterContainer
. The contents of the control referenced in the BodyContainer
property are rendered inside the Margin rectangle on the page, the HeaderContainer
and FooterContainer
(if specified) are rendered directly above and beneath the Margin rectangle respectively.
So, you will have to add two extra Panels to the Form inside which, you will add the controls that are to appear as header and footer. Then, set HeaderContainer
to the header panel, BodyContainer
to the Form and FooterContainer
to the footer panel, making sure that they are not visible on the surface of the Form by setting their Location appropriately (eg. to (-1000, -1000)
) or making them visible only while printing.
Margin widths are controlled by the Margins
property of the PageSettings
object. To set margin widths:
- Set the
PrintDocument.DefaultPageSettings.Margins
property to control the default margin size of all pages printed by the PrintDocument
ByVal e As System.EventArgs) Handles MyBase.Load
Dim newMargins As System.Drawing.Printing.Margins
newMargins = New System.Drawing.Printing.Margins(50, 50, 50, 50)
Me.PrintForm1.DefaultPageSettings.Margins = newMargins
- or set
e.PageSettings.Margins
in the PrintDocument.QueryPageSettings
event to set each printed page's Margin size individually:
ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) Handles
PrintForm1.QueryPageSettings
Dim newMargins As System.Drawing.Printing.Margins
newMargins = New System.Drawing.Printing.Margins(50, 50, 50, 50)
e.PageSettings.Margins = newMargins
The borders of the outermost control you are rendering can be turned on or off using the PrintForm.PrintBorders
property.
You may have to print different controls from a single Form on each page of the printout. You can do this by changing the BodyContainer
property of PrintForm
for each page.
You can also print different Forms on each page of the printout, by adding a PrintForm
to each form, with its BodyContainer
set to the entire Form. Add a main Form to the project, containing a PrintChainManager
component and add a reference to each of the PrintForm
controls to the PrintChainManager
.Documents
collection.
You can print more than one Form or container control on the same page by using the PrintForm.PrintControl()
method which allows you to render any control you like at any position on the page.
To print a Form which is larger than the paper size over multiple pages, the PrintForm.PrintControl
method is used to render a portion of the Form into the MarginBounds
rectangle on each successive page. The Form is divided into a number of 'Tiles
' each with an offset from the origin (top left) of the control. To print each Tile repeatedly print the Form, negatively offset by the specified amount, while the Graphics.Clip
region is set to the page MarginBounds
rectangle to exclude any portion of the Control not in the current Tile.
To print a control inside a UserControl
with a different BackColor
, set UseLegacyPrinting
, BackColorWhilePrinting
, VisibleWhilePrinting
settings on controls that are inside a UserControl
.
It is very often required to print a Form or control scaled to custom size.It can be done as follows:
- Drag a
PrintForm
component onto your Form - Set the
PrintForm.BodyContainer
property to reference your Form - Set
PrintForm.AutoFit = None
, to prevent PrintForm
from automatically scaling up the Form to fit the page. - Set
PrintForm.ManualZoom
to the scale you want, say 0.5 - Set
PrintForm.CenterStyle
to control how the image is centered on the page. - Add a button and button click handler. In the handler call
PrintForm.Print()
Or use the PrintForm.PrintControl()
method and set the scale of the output in the 4th parameter.
You may, at times, want to print a hidden Form / control, for example, where the layout of the printed form is required to be different to the user interface. For this, you can simply call PrintForm.Print()
without displaying the Form, once you have setup the required PrintForm
properties. Basically, the form is visible, but just out of view.
Printing to a file
To print the Page image to a file, use a specialization of the .NET class PrintController
, called FilePrintController
. FilePrintController
supplies a Graphics
object obtained from a Bitmap
to the standard printing mechanism. This class can be used to print to any of the file formats .NET supports even if you were not using PrintForm
to render your printed output.
You could use the following code to achieve this:
Dim target As Control = Me
Dim r As Rectangle = New Rectangle(New Point(0, 0), target.Size)
Dim img As New Bitmap(r.Width, r.Height)
Dim g As Graphics = Graphics.FromImage(img)
Me.PrintForm1.PrintControl(g, r, target, 1.0)
g.Dispose()
img.Save("c:\image.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
img.Dispose()
Similarly, you can print multiple Page images to a TIFF file, using the above class.
To print without showing the 'Printing Page' dialog, use StandardPrintController
. Set the PrintController
property of PrintForm
as follows, before calling Print
, or doing a PrintPreview
:
Me.PrintForm1.PrintController = New
System.Drawing.Printing.StandardPrintController()
Some Problems
You may find that some of the controls on the Form aren't displayed at print. This is because some controls in .NET and some third party controls are not fully managed or are not written according to the .NET control guidelines. So, they are marked as 'Legacy' controls. Select the control and set it's UseLegacyPrinting
property to true
. This should solve the problem. A very few legacy controls may not respond to the methods that PrintForm
uses to get them to render themselves into the printout.
Private Sub PrintForm1_PreDraw(ByVal e As _
TMGDevelopment.PrintForm.PreDrawEventArgs) Handles PrintForm1.PreDraw
If e.Control Is Me.NChart1 Then
Dim vFile As Object
Dim bUseWindowDim, bOK As Boolean
Dim nHeight, nWidth, nBitsPerPixel As Integer
bUseWindowDim = True
nBitsPerPixel = 24
nHeight = e.Bounds.Height
nWidth = e.Bounds.Width
vFile = ""
NChart1.ImportExport.SaveBitmapImage(vFile, bUseWindowDim, nWidth,
nHeight, nBitsPerPixel)
Dim iData As IDataObject = Clipboard.GetDataObject()
Dim img As System.Drawing.Bitmap = iData.GetData(DataFormats.Bitmap, True)
e.Graphics.DrawImage(img, e.Bounds)
e.OwnerDrawn = True
End If
End Sub
The control may offer some other mechanism for obtaining an image of it's contents. If this is the case PrintForm
provides an event, the PreDraw
event, that you can handle to take charge of rendering the control yourself. This has been shown in the above code snippet.
You may also need to distribute the PrintForm
and printchaining assemblies with your application. There are several ways to locate the correct assemblies:
- Build an installer project for your application and it will automatically pull in the correct versions.
Or set copy local to true
for the printform
and printchaining references in your project, this will cause VS.NET to copy them through to your bin directory and you can pick them up from there with your EXE and copy them to the same location as your EXE on the target machine. Or copy them manually from their installed locations
CONCLUSION
Printing Windows forms in VB.NET using the PrintForm
Component is a very simple and an effective way of implementing VB-type single-line printform()
method in VB.NET. I hope my attempt proves useful to people working in this direction. I shall try to update the article with more examples and samples.