Most Visual Basic .NET programs will never need to use the .NET Framework’s native printing capabilities. Reporting tools such as Crystal Reports, as well as RAD tools for laying out reports, provide most of the printing facilities that typical Visual Basic .NET programs need. However, for the cases in which a reporting tool is not flexible enough, this section describes the .NET Framework’s support for outputting text and graphics directly to a printer.
Example 4-13 shows a minimal printing example.
Example 4-13. Hello, Printer!
Imports System.Drawing Imports System.Drawing.Drawing2D Imports System.Drawing.Printing ' ... ' These two lines initiate printing. Place this code in an ' appropriate place in the application. Dim pd As New HelloPrintDocument( ) pd.Print( ) ' ... ' This class manages the printing process. Public Class HelloPrintDocument Inherits PrintDocument Protected Overrides Sub OnPrintPage(ByVal e As PrintPageEventArgs) MyBase.OnPrintPage(e) ' Draw text to the printer graphics device. Dim fnt As New Font("Arial", 10, FontStyle.Regular, _ GraphicsUnit.Point) e.Graphics.DrawString("Hello, Printer!", fnt, Brushes.Black, 0, 0) fnt.Dispose( ) ' Indicate that there are no more pages. e.HasMorePages = False End Sub End Class
Printing is managed by defining a class that inherits from the
PrintDocument class (defined in the
System.Drawing.Printing namespace). Printing is initiated by
instantiating the derived class and calling its Print method
(inherited from the PrintDocument class). The Print method repeatedly
calls the OnPrintPage method, until the HasMorePages property of the
PrintPageEventArgs parameter is set to False
. It
is the job of the OnPrintPage method to generate each page of output
that is sent to the printer.
Take a closer look at the OnPrintPage method in Example 4-13, starting with the first line:
MyBase.OnPrintPage(e)
This line of code calls the OnPrintPage method implemented by the base PrintDocument class, passing it the same argument that was passed into the derived class’s OnPrintPage method. This call is important because the PrintDocument class’s OnPrintPage method is responsible for firing the PrintDocument object’s PrintPage event. If this is not done, any code that has registered for this event will not receive it. This is not an issue in this small example, but it’s nevertheless good programming practice.
The next three lines of code are responsible for handling the printing:
' Draw text to the printer graphics device. Dim fnt As New Font("Arial", 10, FontStyle.Regular, _ GraphicsUnit.Point) e.Graphics.DrawString("Hello, Printer!", fnt, Brushes.Black, 0, 0) fnt.Dispose( )
This code draws some text to the Graphics object provided through the PrintPageEventArgs parameter. Everything you learned in the previous section about the Graphics object is applicable here.
Finally, since we’re printing just one page in our example
program, PrintPageEventArgs.HasMorePages is set to
False
:
' Indicate that there are no more pages. e.HasMorePages = False
This line indicates to the Print method that the end of the document
has been reached. If more pages need to be printed, the OnPrintPage
method should set the HasMorePages property to
True
.
The PrintPageEventArgs class is declared in the System.Drawing.Printing namespace as:
Public Class PrintPageEventArgs Inherits System.EventArgs
Its properties are:
- Cancel
An indication of whether the print job is being canceled. This property is set to
True
by the printing system if the user cancels the print job. Code in the OnPrintPage method can read this value and take any appropriate action. However, programmatically setting this property back toFalse
does not resume the print job. On the other hand, programmatically setting it toTrue
does cancel the print job, even if the user has not clicked the Cancel button. The syntax of the Cancel property is:Public Property Cancel( ) As Boolean
- Graphics
The Graphics object that represents the page surface. The syntax of the Graphics property is:
Public ReadOnly Property Graphics( ) As System.Drawing.Graphics
- HasMorePages
The mechanism for the OnPrintPage method to indicate to the printing system whether there are more pages to be printed after the current page. The OnPrintPage method should set this property to
True
when there are more pages to print and toFalse
when there are no more pages to print. The syntax of the HasMorePages property is:Public Property HasMorePages( ) As Boolean
- MarginBounds
A rectangle that specifies the area of the page that is within the document margins (i.e., the area of the page on which rendering should occur). The syntax of the MarginBounds property is:
Public ReadOnly Property MarginBounds( ) As System.Drawing.Rectangle
- PageBounds
A rectangle that specifies the full area of the page, including the area outside the margins. The syntax of the PageBounds property is:
Public ReadOnly Property PageBounds( ) As System.Drawing.Rectangle
- PageSettings
The page settings that apply to the page currently being printed. The syntax of the PageSettings property is:
Public ReadOnly Property PageSettings( ) As _ System.Drawing.Printing.PageSettings
The PageSettings class is described later in this section.
The PrintDocument class provides the OnBeginPrint and OnEndPrint methods for managing the start and finish of print jobs. The OnBeginPrint method is called prior to the first call to OnPrintPage, and the OnEndPrint method is called after the final call to OnPrintPage. The OnBeginPrint method is a good place to set up objects that will be used throughout the life of the print job—pens, brushes, and fonts, for example. The HelloPrintDocument class in Example 4-13 instantiates a Font object during the OnPrintPage method. This is acceptable here because only one page is being printed. However, in practice documents may contain many pages, so it is better to move this code to the OnBeginPrint method. Example 4-14 shows how the HelloPrintDocument looks when modified in this way.
Example 4-14. Using OnBeginPrint and OnEndPrint to set up and tear down objects used during printing
Public Class HelloPrintDocument Inherits PrintDocument ' Private member to hold the font that will be used for printing. Private m_fnt As Font Protected Overrides Sub OnBeginPrint(ByVal e As PrintEventArgs) MyBase.OnBeginPrint(e) ' Create the font that will be used for printing. m_fnt = New Font("Arial", 10, FontStyle.Regular, _ GraphicsUnit.Point) End Sub Protected Overrides Sub OnEndPrint(ByVal e As PrintEventArgs) MyBase.OnEndPrint(e) ' Release the font. m_fnt.Dispose( ) End Sub Protected Overrides Sub OnPrintPage(ByVal e As PrintPageEventArgs) MyBase.OnPrintPage(e) ' Draw text to the printer graphics device. Dim rect As Rectangle = e.MarginBounds e.Graphics.DrawString("Hello, Printer!", m_fnt, Brushes.Black, 0, 0) ' Indicate that there are no more pages. e.HasMorePages = False End Sub End Class
The code given in Examples Example 4-13 and Example 4-14 merely prints to the default printer. To allow the user to select a specific printer and set other printer options, pass the PrintDocument object to a PrintDialog object and call the PrintDialog object’s ShowDialog method. The ShowDialog method displays a PrintDialog dialog box (shown in Figure 5-19 in Chapter 5). When the user clicks OK in the PrintDialog dialog box, the ShowDialog method sets the appropriate values in the given PrintDocument object. The PrintDocument object’s Print method can then be called to print the document to the selected printer. Here is the code:
' Create the PrintDocument object and the dialog box object. Dim pd As New HelloPrintDocument( ) Dim dlg As New PrintDialog( ) ' Pass the PrintDocument object to the dialog box object. dlg.Document = pd ' Show the dialog box. Be sure to test the result so that printing ' occurs only if the user clicks OK. If dlg.ShowDialog = DialogResult.OK Then ' Print the document. pd.Print( ) End If
This code assumes the presence of the HelloPrintDocument class defined in Example 4-13 or Example 4-14. Note that the HelloPrintDocument class itself does not need to be modified to support choosing a printer.
As mentioned earlier, the PrintPageEventArgs object passed to the OnPrintPage method has a PageSettings property that holds a PageSettings object. This object holds the settings applicable to printing a single page. The properties of the PageSettings class are:
- Bounds
Represents a rectangle that specifies the full area of the page, including the area outside the margins. This is the same value found in the PageBounds property of the PrintPageEventArgs class. The syntax of the Bounds property is:
Public ReadOnly Property Bounds( ) As System.Drawing.Rectangle
- Color
Indicates whether the page should be printed in color. The syntax of the Color property is:
Public Property Color( ) As Boolean
- Landscape
Indicates whether the page is being printed in landscape orientation. The syntax of the Landscape property is:
Public Property Landscape( ) As Boolean
- Margins
Indicates the size of the margins. The syntax of the Margins property is:
Public Property Margins( ) As System.Drawing.Printing.Margins
The Margins class has four properties, Left, Top, Right, and Bottom, each of which is an Integer expressing the size of the respective margin.
- PaperSize
Indicates the size of the paper. The syntax of the PaperSize property is:
Public Property PaperSize( ) As System.Drawing.Printing.PaperSize
The PaperSize class has four properties:
- Width
An Integer expressing the width of the paper. This is the same value found in the Width member of the Bounds property of the PageSettings object.
- Height
An Integer expressing the height of the paper. This is the same value found in the Height member of the Bounds property of the PageSettings object.
- Kind
An enumeration of type PaperKind expressing the size of the paper in terms of standard named sizes, such as
Letter
andLegal
.- PaperName
A string giving the name of the paper size, such as
"Letter"
and"Legal"
.
- PaperSource
Indicates the paper tray from which the page will be printed. The syntax of the PaperSource property is:
Public Property PaperSource( ) As System.Drawing.Printing.PaperSource
The PaperSource class has two properties:
- Kind
An enumeration of type PaperSourceKind expressing the paper source in terms of standard names, such as
Lower
andUpper
.- SourceName
A string giving the name of the paper source, such as
"Lower"
and"Upper"
.
- PrinterResolution
Indicates the resolution capability of the printer. The syntax of the PrinterResolution property is:
Public Property PrinterResolution( ) As _ System.Drawing.Printing.PrinterResolution
The PrinterResolution class has three properties:
- X
An Integer expressing the horizontal resolution of the printer in dots per inch.
- Y
An Integer expressing the vertical resolution of the printer in dots per inch.
- Kind
An enumeration of type PrinterResolutionKind expressing the resolution mode. The values of this enumeration are
Draft
,Low
,Medium
,High
, andCustom
.
- PrinterSettings
Indicates the settings applicable to the printer being used. The syntax of the PrinterSettings property is:
Public Property PrinterSettings( ) As _ System.Drawing.Printing.PrinterSettings
The PrinterSettings class holds values that describe the capabilities and settings of a specific printer. It exposes these properties:
- CanDuplex
Indicates whether the printer can print on both sides of the paper. The syntax of the CanDuplex property is:
Public ReadOnly Property CanDuplex( ) As Boolean
- Collate
Indicates whether the document being printed will be collated. The syntax of the Collate property is:
Public Property Collate( ) As Boolean
- Copies
Indicates the number of copies to print. The syntax of the Copies property is:
Public Property Copies( ) As Short
- DefaultPageSettings
Indicates the default page settings for this printer. The syntax of the DefaultPageSettings property is:
Public ReadOnly Property DefaultPageSettings( ) As _ System.Drawing.Printing.PageSettings
The PageSettings class was described in the previous section.
- Duplex
Indicates whether the print job is to print on both sides of the paper. The syntax of the Duplex property is:
Public Property Duplex( ) As System.Drawing.Printing.Duplex
The Duplex type is an enumeration with the following values:
-
Simplex
The document will print only on one side of each page.
-
Horizontal
The document will print using both sides of each page.
-
Vertical
The document will print using both sides of each page, and the second side will be inverted to work with vertical binding.
-
Default
The document will print using the printer’s default duplex mode.
-
- FromPage
Specifies the first page to print if the PrintRange property is set to
SomePages
. The syntax of the FromPage property is:Public Property FromPage( ) As Integer
- InstalledPrinters
Indicates the names of the printers installed on the computer. This list includes only the printers physically connected to the machine (if any), not necessarily all printers set up in the Control Panel. The syntax of the InstalledPrinters property is:
Public Shared ReadOnly Property InstalledPrinters( ) As _ System.Drawing.Printing.PrinterSettings.StringCollection
The StringCollection class is a collection of strings. It can be iterated using code such as this:
' Assume pts is of type PrinterSettings. Dim str As String For Each str In pts.InstalledPrinters Console.WriteLine(str) Next
- IsDefaultPrinter
Indicates whether this printer is the user’s default printer. The syntax of the IsDefaultPrinter property is:
Public ReadOnly Property IsDefaultPrinter( ) As Boolean
If the PrinterName property is explicitly set to anything other than
Nothing
, this property always returnsFalse
.- IsPlotter
Indicates whether this printer is a plotter. The syntax of the IsPlotter property is:
Public ReadOnly Property IsPlotter( ) As Boolean
- IsValid
Indicates whether the PrinterName property designates a valid printer. The syntax of the IsValid property is:
Public ReadOnly Property IsValid( ) As Boolean
This property is useful if the PrinterName property is being set explicitly. If the PrinterName is set as a result of allowing the user to select a printer through the PrintDialog dialog box, this property will always be
True
.- LandscapeAngle
Indicates the angle (in degrees) by which portrait orientation is rotated to produce landscape orientation. The syntax of the LandscapeAngle property is:
Public ReadOnly Property LandscapeAngle( ) As Integer
This value can only be 90 or 270. If landscape orientation is not supported, this value can only be 0.
- MaximumCopies
Indicates the maximum number of copies that the printer can print at one time. The syntax of the MaximumCopies property is:
Public ReadOnly Property MaximumCopies( ) As Integer
- MaximumPage
Indicates the highest page number that can be entered in a PrintDialog dialog box. The syntax of the MaximumPage property is:
Public Property MaximumPage( ) As Integer
Set this value prior to calling the PrintDialog object’s ShowDialog method to prohibit the user from entering a page number that is too high.
- MinimumPage
Indicates the lowest page number that can be entered in a PrintDialog dialog box. The syntax of the MinimumPage property is:
Public Property MinimumPage( ) As Integer
Set this value prior to calling the PrintDialog object’s ShowDialog method to prohibit the user from entering a page number that is too low.
- PaperSizes
Indicates the paper sizes that are supported by this printer. The syntax of the PaperSizes property is:
Public ReadOnly Property PaperSizes( ) As _ System.Drawing.Printing.PrinterSettings+PaperSizeCollection
The PaperSizeCollection is a collection of objects of type PaperSize. The PaperSize type was described in the previous section, under the description for the PaperSize property of the PageSettings class. The PaperSizeCollection can be iterated using the following code:
' Assume pts is of type PrinterSettings. Dim pprSize As PaperSize For Each pprSize In pts.PaperSizes Console.WriteLine(pprSize.PaperName) Next
- PaperSources
Indicates the paper sources that are available on the printer. The syntax of the PaperSources property is:
Public ReadOnly Property PaperSources( ) As _ System.Drawing.Printing.PrinterSettings+PaperSourceCollection
The PaperSourceCollection is a collection of objects of type PaperSource. The PaperSource type was described in the previous section, under the description for the PaperSource property of the PageSettings class. The PaperSourceCollection can be iterated using the following code:
' Assume pts is of type PrinterSettings. Dim pprSource As PaperSource For Each pprSource In pts.PaperSources Console.WriteLine(pprSource.SourceName) Next
- PrinterName
Indicates the name of the printer. The syntax of the PrinterName property is:
Public Property PrinterName( ) As String
Unless a string has been explicitly assigned to the property, its value is
Null
.- PrinterResolutions
Indicates the resolution supported by this printer. The syntax of the PrinterResolutions property is:
Public ReadOnly Property PrinterResolutions( ) As _ System.Drawing.Printing.PrinterSettings.PrinterResolutionCollection
The PrinterResolutionCollection is a collection of objects of type PrinterResolution. The PrinterResolution type was described in the previous section, under the description for the PrinterResolution property of the PageSettings class. The PrinterResolutionCollection can be iterated using the following code:
' Assume pts is of type PrinterSettings. Dim ptrResolution As PrinterResolution For Each ptrResolution In pts.PrinterResolutions Console.WriteLine(ptrResolution.Kind.ToString( )) Next
- PrintRange
Indicates the range of pages that are to be printed. The syntax of the PrintRange property is:
Public Property PrintRange( ) As System.Drawing.Printing.PrintRange
The PrintRange type is an enumeration having the following values:
-
AllPages
Prints the entire document.
-
Selection
Prints only the selected portion of the document.
-
SomePages
Prints the pages starting at the page specified in the FromPage property and ending at the page specified in the ToPage property.
-
- SupportsColor
Indicates whether the printer supports color printing. The syntax of the SupportsColor property is:
Public ReadOnly Property SupportsColor( ) As Boolean
- ToPage
Specifies the final page to print if the PrintRange property is set to
SomePages
. The syntax of the ToPage property is:Public Property ToPage( ) As Integer
The methods of the PrinterSettings class are:
- GetHdevmode
Returns a handle to a Windows
DEVMODE
(device mode) structure corresponding to this PrinterSettings object. The GetHdevmode method has two forms:Public Overloads Function GetHdevmode( ) As System.IntPtr
and:
Public Overloads Function GetHdevmode( _ ByVal
pageSettings
As System.Drawing.Printing.PageSettings _ ) As System.IntPtrThe
DEVMODE
structure is part of the Windows API and is not discussed further in this book.- GetHdevnames
Returns a handle to a Windows
DEVNAMES
structure corresponding to this PrinterSettings object. The syntax of the GetHdevnames method is:Public Function GetHdevnames( ) As System.IntPtr
The
DEVNAMES
structure is part of the Windows API and is not discussed further in this book.- SetHdevmode
Sets properties of this PrinterSettings object based on values in the given
DEVMODE
structure. The syntax of the SetHdevmode method is:Public Sub SetHdevmode(ByVal
hdevmode
As System.IntPtr)The
DEVMODE
structure is part of the Windows API and is not discussed further in this book.- SetHdevnames
Sets properties of this PrinterSettings object based on values in the given
DEVNAMES
structure. The syntax of the SetHdevnames method is:Public Sub SetHdevnames(ByVal
hdevnames
As System.IntPtr)The
DEVNAMES
structure is part of the Windows API and is not discussed further in this book.
Windows Forms provides a common dialog box for page setup (shown in Figure 5-18 in Chapter 5). Settings entered by the user in this dialog box are saved to a PageSettings object. This PageSettings object can be saved by the application and passed to the PrintDocument object prior to calling the PrintDocument object’s Print method. The PrintDocument object will then use the given settings for printing. Here’s the code that displays the dialog box:
Private Sub ShowPageSetup( ) ' Display the page settings dialog box. This assumes that there is ' a class-level variable of type PageSettings called m_pgSettings. Dim pgSetupDlg As New PageSetupDialog( ) pgSetupDlg.PageSettings = m_pgSettings pgSetupDlg.ShowDialog( ) End Sub
This code depends on the existence of a class-level variable of type PageSettings called m_pgSettings. Here is a suitable definition for this variable:
' Private member to hold the application's page settings. ' This could be placed in an application's main form or in another ' class that is accessible to the code that will need to print. Private m_pgSettings As New PageSettings( )
Note the use of the New
keyword to ensure that the
PageSettings object is instantiated as soon as the enclosing class is
instantiated.
All that remains is to hand the PageSettings object to the PrintDocument object when it’s time to print. Here is the code:
Private Sub PrintTheDocument( ) ' Create the PrintDocument object. Dim pd As New HelloPrintDocument( ) ' Hand it the PageSettings object. pd.DefaultPageSettings = m_pgSettings ' Create the dialog box object. Dim dlg As New PrintDialog( ) ' Pass the PrintDocument object to the dialog box object. dlg.Document = pd ' Show the dialog box. Be sure to test the result so that printing ' occurs only if the user clicks OK. If dlg.ShowDialog = DialogResult( ).OK Then ' Print the document. pd.Print( ) End If End Sub
Generating a print preview is trivial. An instance of the PrintDocument object is created and passed to a PrintPreviewDialog object, whose ShowDialog method is then called to show the print preview. Here is the code:
Private Sub ShowPrintPreview( ) ' Create the PrintDocument object. Dim pd As New HelloPrintDocument( ) ' Hand it the PageSettings object. pd.DefaultPageSettings = m_pgSettings ' Create the print preview dialog box object. Dim dlg As New PrintPreviewDialog( ) ' Pass the PrintDocument object to the dialog box object. dlg.Document = pd ' Show the dialog box. dlg.ShowDialog( ) End Sub
The result is shown in Figure 4-16.
The .NET Framework hides the mechanics of printing. Applications don’t have to know how to find printers, they don’t have to know what a given printer’s capabilities are, and they don’t have to know how to issue commands that are meaningful to a given brand of printer. The Framework abstracts all of this away. However, the Framework doesn’t know anything about a given application’s documents. It is up to the application developer to know how to paginate the application’s documents and render each page in response to each call to the PrintDocument class’s OnPrintPage method.
Get Programming Visual Basic .NET now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.