Click here to Skip to main content
Click here to Skip to main content

Form Print Control

By , 21 Jan 2009
 

Introduction

One of the most common questions is "how can I print / print preview my form"? This is quite easily done by getting the form to "draw itself" on the printed page, 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 with a bunch of new properties (look in the Properties window for each control in the section named "MCL Form Print Helper").

Printing helper extended properties

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.

Extended properties provided by the control

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

    '\\ The application code should draw this 
    '\\ control because it is special....
    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 add a reference to the control's DLL to your application, and (optionally) add it to the toolbox.

<iframe class="Silverlight" width="400" height="500" 
  src="http://silverlight.services.live.com/invoke/65357/
       Add%20Reference%20To%20The%20MCL%20Form%20Print%20Helper%20component/iframe.html" 
  frameBorder="0" + scrolling="no"></iframe>

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.

<iframe class="Silverlight" width="400" height="500" 
  src="http://silverlight.services.live.com/invoke/65357/
       Set%20RTFBox%20print%20mode/iframe.html" 
  frameBorder="0" + scrolling="no"></iframe>

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".

<iframe class="Silverlight" width="400" height="500" 
  src="http://silverlight.services.live.com/invoke/65357/
       Show%20Gridlines%20On%20Preview/iframe.html" 
  frameBorder="0" + scrolling="no"></iframe>

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Duncan Edwards Jones
Software Developer (Senior) JP Morgan
Ireland Ireland
Member
C# / SQL Server developer
Microsoft MVP 2006, 2007
Visual Basic .NET

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: Setting printermemberGalena13 May '08 - 7:24 
Hi Duncan,
 
This is WONDERFUL!!!
 
You didn't say where to put this code, so I put it at the end of the "Component properties" region.
 
I found that I needed to change the third line from the bottom to:
 
PrintDocument_Form.PrinterSettings.PrinterName = value
 
Perhaps this is because I inserted the block at the wrong spot. In any case, it does precisely what I need, with the line in my code of the form:
 
FormPrintHelperComponent1.DefaultPrinter = "Adobe PDF"
 
Thanks!
 
Sam
GeneralFont size, VB 2008, printermemberGalena19 Apr '08 - 7:53 
This is a wonderful program, but I have some questions.
 
1. My immediate question is: Can I dynamically change the font size of a text box to be printed with MCL form print, similar to the following?
 
ATextBox.PrintFont = MyFont
 
2. I'm using MCL Form Print with VB 2008 Express on Vista. VB 2008 does the usual conversion. Since then, the Page Setup diaglog doesn't work. I still can't find the multiple page stuff. I print multiple pages by repeatedly calling FormPrintHelperComponent1.Print(), but this creates a separate job for each page, a pest when printing to Adobe PDF.
 
3. The download Zip file I have contains files with mod dates as late as 10-12-2007 11:56 AM. Is this the latest? Do you have plans for a VB 2008 version and any changes needed for Vista?
 
4. I'd like to specify the printer. I have found a way to change the Windows default printer, which MCL form print will use, but is there a way for the code to specify which printer to use for a specific print?
 
Thanks!
GeneralRe: Font size, VB 2008, printermemberDuncan Edwards Jones19 Apr '08 - 11:26 
Hi
 
The most recent release (Release 0.5 on CodePlex[^]) is in VB2008
 
To print multiple logical pages (that is to say different page templates) you need to set the LogicalPages property to indicate the number of logical pages you want. Then you set the MultiPagePrintMethod and PrintOnPages properties for each control on your form to decide what page(s) they shoud print on.
 
To have a page overflow (i.e. if the data are too big for one page then generate another new physical page) set the DataOverflowAction for the form control concerned to: CreateNewPage
 
'--8<------------------------
Ex Datis:
Duncan Jones
Merrion Computing Ltd

GeneralRe: Font size, VB 2008, printermemberGalena19 Apr '08 - 13:00 
Hi Duncan,
 
Thanks for your help. I hope you can continue to tolerate someone less expert in this area.
 
I downloaded and installed 0.5. Running the test code, with no changes, the Print Preview works. The Print button does nothing. When I click on Save As, I get an exception in PrintControlCodeWriter.vb at the lines:
 
If Not _generator Is Nothing Then
_writer = New System.IO.StreamWriter(fi.FullName)
 
The exception is DirectoryNotFound with:
Could not find a part of the path 'C:\temp\duncan.vb'.
 
When I give it the temp directory, Save As does produce the duncan.vb file, but I don't know what to do with it.
 
Sam
GeneralRe: Font size, VB 2008, printermemberDuncan Edwards Jones19 Apr '08 - 23:54 
Ah - the "Save As" functionality is very far from complete. The putative aim is to generate visual basic classes that when compiled print the document as designed for the FormPrintComponent - i.e allowing print document generation from sources other than windows forms ... like command line or UI-less applications.
 
Unfortunately the click event of the print button had gone amiss. The code in the Form1.vb should include:-

Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Me.FormPrintHelperComponent1.Print()
End Sub

 
Hope this helps,
Duncan
 
'--8<------------------------
Ex Datis:
Duncan Jones
Merrion Computing Ltd

GeneralPrint PreviewmemberBrave_Gass4 Feb '08 - 22:48 
I have one picturebox later iam loading dynamically label for picturebox
 
How to print the Picturebox and contents
GeneralRe: Print PreviewmemberDuncan Edwards Jones5 Feb '08 - 8:43 
For a control added after dynamically you should set the print property with FormPrintHelperComponent1.SetPrint(Label1, True) (in the example this would be for your dynamic label called "Label1").
 
Set any other properties in the same manner.
 
The concept of Z-Order has not yet been coded in this control - I'm working on it. Currently it prints things in th eorder they are added so add the picturebox first and then the label and the label will print over the pricture...
 
'--8<------------------------
Ex Datis:
Duncan Jones
Merrion Computing Ltd

GeneralBounding Rectangle always 0,0,0,0memberMember 465625022 Jan '08 - 6:24 
When dragging a new control onto form, the bounding rectangle is set to x=0,y=0,width=0 and height=0. Isn't it supposed to inherit the values from the position and size of the control on the form?
GeneralRe: Bounding Rectangle always 0,0,0,0memberDuncan Edwards Jones22 Jan '08 - 9:43 
Yup - it's a bit poor in that respect.
 
When I get the size of a newly added control it is 0,0,0,0 - it seems it does add then resize operation? Adding the component to a form that already has all the other controls on it seems to work OK.
 
'--8<------------------------
Ex Datis:
Duncan Jones
Merrion Computing Ltd

GeneralRe: Bounding Rectangle always 0,0,0,0memberGrahamEd22 Jan '08 - 23:18 
Ah got it. I was just testing to see how it works before using it with an existing form. Now I know I can add the component later and save myself a lot of time.
 
Its a great feature by the way, thanks for writing it.
 
Oh one more thing, I am also having difficulty aligning (horizontally) a label control with a text box control. The textbox control always appears higher, no matter what I do with the vertical postioning.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 21 Jan 2009
Article Copyright 2007 by Duncan Edwards Jones
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid