Printing

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.

Hello, 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

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 to False does not resume the print job. On the other hand, programmatically setting it to True 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 to False 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 OnBeginPrint and OnEndPrint Methods

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

Choosing a Printer

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.

The PageSettings Class

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 and Legal.

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 and Upper.

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, and Custom.

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 is described in the next section.

The PrinterSettings Class

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 returns False.

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:

Clone

Creates a copy of the PrinterSettings object. The syntax of the Clone method is:

Public NotOverridable Function Clone(  ) As Object _
   Implements System.ICloneable.Clone
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.IntPtr

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

Page Setup Dialog Box

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

Print Preview

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.

Hello, Printer! in a print preview window

Figure 4-16. Hello, Printer! in a print preview window

Summary of Printing

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.