Introduction
One of the most common questions is "how can I print / print preview my Windows Form"? This is quite easily done by getting the form to "draw itself" on the printed page or using the VB Power Packs to do likewise, but this is often unsatisfactory because:
- the location of components on the form may not be where you want them on the printed page
- text is drawn at screen resolution
- every control is drawn (e.g., buttons, the form border, etc.)
The attached component addresses the problem in a different manner.... What you do is drop a FormPrintHelperComponent
component onto your form and it extends each control on that form with a set of new properties (look in the Properties window for each control in the section named "MCL Form Print Helper").
You then set these to indicate:
- whether or not the control should be printed
- whether to print it as Text, Image, or Owner Drawn
- the boundary rectangle to print it in
- the vertical / horizontal alignment within that area
- Font and background/foreground colour to use, etc.
The component then allows you to preview the printed page at design time, and to preview or print it at run time.
How It Works
The component implements System.ComponentModel.IExtenderProvider
which means it provides properties to the other controls on the form - in the same way as the built-in ToolTip and F1 Help provider components do.
The class is decorated with ProvideProperty
attributes which tells the IDE which properties the control provides to extend the properties of the controls on the form. Underlying each of these "properties" are a matched pair of a function and sub routine to get/set the property for the control passed in, which are called if an extended property is changed in the target control's property window.
When the component is invoked to print or print preview, it iterates through all the controls on the form that have the extended property Print
set to true
. It then prints the control according to the other extended properties provided for that control such as boundary rectangle, font, foreground and background colour, etc.
The PrintMode
extended property affects how the control is printed. There are three options:
PrintAsText
- Prints the "Text
" property of the control PrintAsImage
- Prints the "Image
" property of the control if it has one; otherwise, prints the control as a bitmap exactly as it appears on screen PrintAsOwnerDrawn
- Passes the control to the OwnerDrawnControlprint
event so the application can decide how to print the control
"Print as Image" Printing Functionality
If the control doesn't have an Image
attribute, the control is drawn onto a bitmap, which is then printed on the form by using the control class' DrawToBitmap
method:
Private Function GetControlImage(ByVal ctlIn As Control) As Image
Dim Imageproperty As System.Reflection.PropertyInfo
Imageproperty = ctlIn.GetType.GetProperty("Image", GetType(Image))
If Imageproperty Is Nothing Then
Dim imgNew As New Bitmap(ctlIn.Width, ctlIn.Height)
ctlIn.DrawToBitmap(imgNew, ctlIn.ClientRectangle)
Return imgNew
Else
Return CType(Imageproperty.GetValue(ctlIn, Nothing), Image)
End If
End Function
"Owner Drawn" Printing Functionality
If you set the extended property PrintMode
to PrintAsOwnerDrawn
, then at run time, when the print or print preview logic is invoked, an OwnerDrawnControlPrint
event will be raised to allow you to programmatically draw the control onto the page canvas.
Private Sub FormPrintHelperComponent1_OwnerDrawnControlPrint(ByVal sender As Object, _
ByVal e As MCL_Form_Print_Control.OwnerPrintControlEventArgs) _
Handles FormPrintHelperComponent1.OwnerDrawnControlPrint
With e
If .ControlToPrint.Name = "DateTimePicker1" Then
e.Canvas.FillRectangle(Brushes.Aqua, e.BoundingRectangle)
e.Canvas.DrawString(">> " & .ControlToPrint.Text & _
"<<", .ControlToPrint.Font, _
Brushes.Azure, e.BoundingRectangle)
End If
End With
End Sub
Worked Example
First, compile the attached source code and then add a reference to the control's DLL to your application, and (optionally) add it to the toolbox.
The next step is to layout your Windows form in Design mode with all the controls that you want on it. (You can add controls at a later date, and the extender will pick them up, so don't worry about having the full design in place to start with). You then drag an instance of the component from the tool bar on to your form.
Then, select the control(s) you want to print - e.g., on the form in the demo project in the source code above, the controls Label_Databasename
, CheckedListBox_Databases
, etc. are to be printed, but the user does not want to print the buttons, Button1
and Button2
.
On the control properties pane for the controls to be printed, navigate down to the section "MCL Form Print Utility" and set the extended property named "Print on FormPrintHelperComponent1" to true
.
Setting the rich text box mode to PrintAsRichText
means the content of a rich text box is printed in what-you-see-is-what-you-get mode.
Selecting Where to Print the Component on the Page
By default, the property "BoundingRectangle
on FormPrinthelperComponent1
" is set to the same rectangle as the control occupies on the form. You can alter this (to make the layout more suited to the size and orientation of the printed page) by altering the values in this property. To see how this change looks, you can preview the form print layout by selecting the FormPrintHelperComponent
instance, and from its smart tag menu, select the option "Preview Document".
Other Extender Provided Properties
The component provides the following extended properties to the form on which it is sited:
DocumentName
- The name of the document as it will appear in the print queue when printed. This defaults to the name of the form if not set. PaperKind
- The paper size to use to print the document (A4, A5, letter, etc.).
The component provides the following extended properties to all the controls on the form on which the component is sited:
BackColour
- The colour to fill the bounding rectangle of the control before printing it. If this is transparent, then the bounding rectangle will not obscure items printed before it. BoundingRectangle
- Where on the page to print the component. ForeColour
- The foreground colour to use to print the control (this has no effect if the print mode is not PrintAsText
). HorizontalAlignment
, VerticalAlignment
- How to align the control's text relative to its bounding rectangle for printing. Print
- True if it should be printed on the page, false if not. PrintFont
- The font to use to print the control's text if the print method is PrintAsText
. PrintMethod
- How to print the control (see explanation above). TopBorder
, BottomBorder
, LeftBorder
, RightBorder
- Pens to use to draw a border around the bounding rectangle on the printed document.
Multi-Page Documents
Of course, there are many cases where a single form needs to be printed over multiple logical pages - for example, if a form has many tabs on it, you might find it sensible to print one logical page per tab.
The component implements this functionality by having a property LogicalPages
which is a positive number 1 or greater that is the number of logical pages that this form should be printed over. Then, for each control, two additional extended properties are added that control which pages that control is to be printed on: MultiPageprintMethod
which is an enumerated type:
PrintOnEveryPage
- Print the control on every logical page PrintOnOddPages
- Print the control on odd numbered logical pages PrintOnEvenPages
- Print the control on even numbered logical pages PrintOnlyOnSpecifiedLogicalPages
- Print the control only on the specified pages
If the MultiPageprintMethod
is set to PrintOnlyOnSpecifiedLogicalPages
, then a second extended property, PrintOnPages
(which is a list of boolean values, one for each logical page) is used to set the page(s) to print it on.
Logical Pages?
When the data in a control (such as a multi-line textbox, a rich textbox, or a grid) is too large for the area allocated to print it on the document, you can optionally set it to flow over to a new page until it reaches the end of the data by setting the extended property DataOverflowAction
to CreateNewPage
.
However, the new page will be the same logical page as the current one (with all the same controls on it), but will be a different physical page. The use of logical pages at the design stage allows a multi part document to be created that does not need to change at run time to accommodate extra data.
Updates
- Added a
UITypeEditor
to allow you to set the colour and line width of all the border lines - Added an extended property
Trimming
which controls how words wrap within their bounding rectangle - Added an extended property
PropertyToPrint
to allow you to specify which property of the control you want to print - Added a
PrinterSettings
method that invokes the printer setup dialog box to allow the settings to be changed at run time - Added a
PageSetup
method that invokes a page setup dialog box to allow the user to change the page layout at run time - Added multi-page functionality
- Added code to print a rich text box as rich text (what-you-see-is-what-you-get) by setting
RTFPrintMethod
- Added C# version of the component