Just a few years ago, if you wanted to provide print preview and printer behavior, you had to write a lot of custom code for the preview device and the printer device. GDI+ insulates us from writing significantly different code even when we are targeting different devices for our graphics output.
In Visual Basic .NET, code targeted at a Windows Form is almost identical to code to produce the same result on a printer device. Because of the similarities between printing on a form versus a printer, we will not rehash the GDI+ features we have covered. Assume that scaling, translating, rotating, and other vector-based and imaging graphics capabilities just work, even when the target device is a printer. Listing 17.12 demonstrates code similar to earlier examples, but targeted at a printer. (The printer used to test the code was a Hewlett-Packard LaserJet 4.)
Listing 17.12 Vector-based graphics sent directly to the printer
1: Imports System.Drawing.Printing 2: 3: Public Class Form1 4: Inherits System.Windows.Forms.Form 5: 6: [Windows Form Designer generated code] 7: 8: Private Sub Button1_Click(ByVal sender As System.Object, _ 9: ByVal e As System.EventArgs) Handles Button1.Click 10: PrintDocument1.Print() 11: End Sub 12: 13: Private Sub PrintDocument1_PrintPage(ByVal sender As Object, _ 14: ByVal e As System.Drawing.Printing.PrintPageEventArgs) _ 15: Handles PrintDocument1.PrintPage 16: 17: Dim AnImage As Image = Image.FromFile("..\ images\ jacket.jpg") 18: Dim M As New Drawing2D.Matrix() 19: M.Scale(2, 2) 20: e.Graphics.Transform = M 21: e.Graphics.DrawImage(AnImage, 0, 0) 22: 23: End Sub 24: End Class
To prepare the example, add the Imports System.Drawing.Printing namespace as demonstrated on line 1 of Listing 17.12. The easiest means of accessing the default printer is to add a PrinterDocument control to the form. (The PrinterDocument control will be added to the component tray.) The code example shows that the default name PrinterDocument1 was kept.
The sample program, PrinterDemo.sln, calls PrintDocument1.Print when Button1_ Click is raised. The real work begins in the PrintPage event handler. One of the properties of the PrintPageEventArgs parameter is a Graphics object. After you have the Graphics object representing the canvas of the printer, you can write your graphics code as if you were displaying the output to a Windows Form.
The code examplebeginning on line 17loads an image from a JPG file. Line 18 creates a Matrix object, and line 19 scales the matrix, M.Scale(2, 2). The Matrix transform is applied to the Graphics object representing the printer's canvas, and DrawImage is called. If you compare Listing 17.12 to Listing 17.8, you will see that the code for displaying graphics on the printer is consistent with code for displaying graphics on a form. In fact, if you literally copy and paste the code from the PrintPage event to the forms Paint event handler, the precise code will display the graphic on the form, too.
Frameworks help organize code into logically associated bits. An object-oriented framework both organizes code and makes it behave in a polymorphic way. In the example, the exact same code displays the graphic on a printed page or a form because the actual Graphics object behaves in a polymorphic way depending on whether the object represents the form's DC or the printer's.
Use the code in earlier examples throughout this chapter to experiment with vector-based printing and bitmapped printing.