The PrintDocument class sits at the heart of Visual Basic’s printing process. The program creates an instance of this class and installs event handlers to catch its events. When the object must perform printing-related tasks, it raises events to ask the program for help.
The PrintDocument object raises four key events:
Having created a PrintDocument object and its event handlers, you can do three things with it. First you can call the object’s Print method to immediately send a printout to the currently selected printer. The PrintDocument object raises its events as necessary as it generates the printout.
Second, you can set a PrintPreviewDialog control’s Document property to the PrintDocument object and then call the dialog box’s ShowDialog method. The PrintPreviewDialog displays the print preview window shown in Figure 27-1, using the PrintDocument object to generate the output it displays.
The preview dialog box’s printer button on the left sends the printout to the printer. Note that this makes the PrintDocument object regenerate the printout using its events, this time sending the results to the printer instead of to the print preview dialog box. The magnifying glass button displays a drop-down list where the user can select various scales for viewing the printout. The next five buttons let the user display one, two, three, four, or six of the printout’s pages at the same time. The Close button closes the dialog box and the Page up/down arrows let the user move through the printout’s pages.
The PrintPreviewControl displays a print preview much as the PrintPreviewDialog control does, except that it sits on your form. It does not provide all the buttons that the dialog box does, but it does provide methods that let you implement similar features. For example, it lets your program set the zoom level, the number of columns in the display, and so forth.
The third thing you can do with a PrintDocument is assign it to a PrintDialog object’s Document property and then call the dialog box’s ShowDialog method. This displays a dialog box that lets the user select the printer and set its properties (for example, selecting landscape or portrait orientation). When the user clicks the dialog box’s Print button, the dialog box uses the PrintDocument object to send the printout to the printer.
Example program UsePrintPreviewDialog uses the following code to preview and print a page showing the page’s bounds and margin bounds. This is just about the smallest program that demonstrates all three uses for a PrintDocument object: printing immediately, displaying a print preview dialog box, and displaying a print dialog box.
Imports System.Drawing.Printing
Public Class Form1
Private WithEvents MyPrintDocument As PrintDocument
Private PageNumber As Integer
' Display a print preview dialog.
Private Sub btnPrintPreview_Click() Handles btnPrintPreview.Click
PageNumber = 1
MyPrintDocument = New PrintDocument
dlgPrintPreview.Document = MyPrintDocument
dlgPrintPreview.ShowDialog()
End Sub
' Display a print dialog.
Private Sub btnPrintDialog_Click() Handles btnPrintDialog.Click
PageNumber = 1
MyPrintDocument = New PrintDocument
dlgPrint.Document = MyPrintDocument
dlgPrint.ShowDialog()
End Sub
' Print now.
Private Sub btnPrintNow_Click() Handles btnPrintNow.Click
PageNumber = 1
MyPrintDocument = New PrintDocument
MyPrintDocument.Print()
End Sub
' Print a page with a diamond on it.
Private Sub MyPrintDocument_PrintPage(
sender As Object, e As PrintPageEventArgs) Handles MyPrintDocument.PrintPage
e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
e.Graphics.TextRenderingHint =
Drawing.Text.TextRenderingHint.AntiAliasGridFit
Using the_font As New Font("Times New Roman", 300)
Using string_format As New StringFormat
string_format.Alignment = StringAlignment.Center
string_format.LineAlignment = StringAlignment.Center
e.Graphics.DrawString(PageNumber.ToString,
the_font, Brushes.Black,
e.MarginBounds, string_format)
End Using ' string_format
End Using ' the_font
Using the_pen As New Pen(Color.Black, 10)
Select Case PageNumber
Case 1 ' Draw a triangle.
Dim points() As Point =
{
New Point(e.MarginBounds.Left + e.MarginBounds.Width 2,
e.MarginBounds.Top),
New Point(e.MarginBounds.Right, e.MarginBounds.Bottom),
New Point(e.MarginBounds.Left, e.MarginBounds.Bottom)
}
e.Graphics.DrawPolygon(the_pen, points)
Case 2 ' Draw a rectangle.
e.Graphics.DrawRectangle(the_pen, e.MarginBounds)
Case 3 ' Draw a diamond.
Dim points() As Point = {
New Point(e.MarginBounds.Left + e.MarginBounds.Width 2,
e.MarginBounds.Top),
New Point(e.MarginBounds.Right,
e.MarginBounds.Top + e.MarginBounds.Height 2),
New Point(e.MarginBounds.Left + e.MarginBounds.Width 2,
e.MarginBounds.Bottom),
New Point(e.MarginBounds.Left,
e.MarginBounds.Top + e.MarginBounds.Height 2)
}
e.Graphics.DrawPolygon(the_pen, points)
End Select
End Using ' the_pen
PageNumber += 1
e.HasMorePages = PageNumber <= 3
End Sub
End Class
The code declares a PrintDocument object named MyPrintDocument. It uses the WithEvents keyword, so it can easily catch the object’s events.
Next, the code defines the variable PageNumber. It uses this variable later to keep track of the page it is printing.
If the user clicks the Print Preview button, the btnPrintPreview_Click event handler resets PageNumber to 1, assigns MyPrintDocument to a new PrintDocument object, sets the PrintPreviewDialog object’s Document property equal to the new PrintDocument object, and invokes the dialog box’s ShowDialog method.
If the user clicks the Print Dialog button, the btnPrintDialog_Click event handler resets PageNumber to 1, assigns MyPrintDocument to a new PrintDocument object, sets the PrintDialog object’s Document property equal to the new PrintDocument object, and calls the dialog box’s ShowDialog method.
If the user clicks the Print Now button, the btnPrintNow_Click event handler resets PageNumber to 1, assigns MyPrintDocument to a new PrintDocument object, and calls its Print method.
In all three cases, the PrintDocument object raises its PrintPage event when it is ready to print a page. The PrintPage event handler shown here demonstrates several techniques that are worth mentioning briefly.
The e.Graphics parameter holds a reference to the Graphics object that the event handler should use to produce the printed page. The section “Graphics Objects” later in this chapter says more about Graphics objects.
The event handler starts by setting the Graphics object’s SmoothingMode property to AntiAlias. That makes the object produce smoother results when it draws lines, ellipses, and other shapes. This slows drawing slightly but, unless the shapes you’re drawing are extremely complex, it doesn’t take too much extra time and the result is usually worth it.
Next, the event handler sets the Graphics object’s TextRenderingHint property to AntiAliasGridFit. This also slows drawing somewhat but usually makes text appear smoother (except for very small fonts).
The event handler then creates a large 300-point font. Like many graphical classes including pens and brushes, the Font class implements the IDisposable method so the program creates the font in a Using statement so its resources are automatically freed when the Using block ends.
Inside the Using block, the program creates a StringFormat object. It sets that object’s Alignment and LineAlignment properties to Center to center text vertically and horizontally. The code then calls the Graphics object’s DrawString method to draw the current page number. It passes DrawString the page’s MarginBounds so it knows to place the text inside the page’s margins. It also passes DrawString the StringFormat object to make it center the text vertically and horizontally within the MarginBounds.
The event handler then creates a 10-pixel-wide black pen. Depending on the current page number, the code draws a triangle, rectangle, or diamond. The following sections provide a bit more information about how the Graphics object’s drawing methods work.
After it has drawn the page, the code increments PageNumber and sets e.HasMorePages to True if it has not yet printed all three pages. Figure 27-1 shows the program displaying its print preview dialog box.