Before we write our first printing application, it's important to understand how printing works in Windows and what role GDI+ plays in the process.
GDI+ is an application-level library that allows applications to interact with display devices such as monitors, printers, and scanners through the device drivers. Figure 11.1 illustrates the role of GDI+ in the drawing process. The application passes data to GDI+. GDI+ is responsible for converting the data into graphics format (pixels) with the help of display drivers and sending it to the display driver, which displays the data on a device such as a monitor.
Figure 11.1. A simple drawing process
The printing process, which is very similar to the drawing process, is shown in Figure 11.2. The application sends data to GDI+, which communicates with a printer driver that sends data to the printer.
Figure 11.2. A simple printing process
11.2.1 How is Drawing Different from Printing?
The drawing process involves a surface, which is the container for graphics shapes. In Windows applications, a form works as a drawing surface. In previous chapters we used the Graphics object associated with a form to access the surface associated with a form.
There are several ways to get the Graphics object associated with a form. The simplest way is to use the form's paint event handler and PaintEventArgs.Graphics property, which returns the Graphics object for the form to which this paint event handler belongs. Another way is to use the CreateGraphics method. Listing 11.1 uses PaintEventArgs.Graphics to get the Graphics object associated with a form. Once you have the drawing surface (Graphics object), you can use draw and fill methods.
Listing 11.1 Drawing graphics shapes
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; SolidBrush redBrush = new SolidBrush(Color.Red); Rectangle rect = new Rectangle(150, 80, 200, 140); g.FillPie(greenBrush, 40, 20, 200, 40, 0.0f, 60.0f ); g.FillRectangle(blueBrush, rect); }
The printing process is somewhat different from the drawing process. In a printing process, a printer works as a drawing surface. In a drawing process, we already have a form as a drawing surface. To print something on a printer, however, we need the printer object. The basic steps of a printing process are
Step 1. Specify the printer you want to use.
Step 2. Retrieve the printer's surface, which is a Graphics object.
Step 3. Call the draw and fill methods of the Graphics object.
In Sections 11.2.2 and 11.2.3 we will discuss the printing process in more detail.
11.2.2 Conceptual Flow of the Printing Process
Before we discuss the programmatic flow of a printing process, let's look at the conceptual flow. Every printing process involves five basic steps, as illustrated in Figure 11.3.
Figure 11.3. Conceptual flow of the printing process
Step 1. Specify a printer. In this step we select a printer to be used in the printing process. You may want to select a printer from multiple printers available to your application.
Step 2. Set the printer properties. In this step we can set properties such as color, paper tray, paper size, and print quality. This step is optional; if we do not set printer properties, the process uses default settings.
Step 3. Get the printer surface. Unlike the drawing surface (a form), which is available on the form's paint event handler, the printer surface is available only through the print-page event handler. As such, this step requires creating a print-page event handler. One parameter of the event handler is of type PrintPageEventArgs, whose Graphics member represents the printer surface associated with this print-page event handler. In Section 11.2.3 we will see how to implement the print-page event handler programmatically.
Step 4. Draw graphics shapes, lines, curves, text, and images. Once we have the printer surface, everything works in much the same way as the drawing process. We can call draw and fill methods to draw lines, curves, shapes, text, and images.
Step 5. Print. After we call the draw and fill methods of the Graphics object associated with a printer, the final step is to print the objects.
11.2.3 Programmatic Flow of the Printing Process
The previous section dealt with the conceptual flow of the printing process. In this section we will examine the programmatic flow.
Figure 11.4 is a flowchart displaying the four programmatic steps of the printing process.
Figure 11.4. A flowchart of the printing process
Step 1. Create a PrintDocument object and specify the printer. This printer will be used as a surface.
Step 2. Set the printer and page properties. We set the PrinterSettings and PageSettings objects for this optional step. If we don't set these properties, the default settings of the printer will be used. We will cover PrinterSettings and PageSettings in more detail later.
Step 3. Set the print-page event handler. The print-page event handler is responsible for printing. We create a print-page event handler by setting the PrintDocument.PrintPage member. Process A (see Figure 11.5) is called from the print-page event handler, as illustrated in Figure 11.4.
Figure 11.5. Process A
Step 4. Print the document. Finally, we call the PrintDocument.Print method, which sends printing objects to the printer.
Process A, which is shown in Figure 11.5, describes how and what to send to the printer. This process is defined as the print-page event handler:
public void pd_PrintPage(object sender, PrintPageEventArgs ev)
The second parameter, PrintPageEventArgs, provides access to the printer surface through its Graphics member. As Figure 11.5 shows, first we get the Graphics object from PrintPageEventArgs.
The next step is to set the page and paper setting using the MarginBounds, PageBounds, and PageSettings members of the PrintPageEventArgs enumeration. We will discuss these properties in more detail later.
The final step of this process is to call draw and fill methods of the Graphics object as we used to do in the drawing process. We will see a working example of this process in Section 11.3.
11.2.4 The System.Drawing.Printing Namespace
In the .NET Framework, printing functionality is defined in the System.Drawing.Printing namespace, which resides in the System.Drawing.dll assembly. The reference to this assembly is automatically added to an application when we create a new project using Visual Studio .NET. To use the printing-related classes, we can simply add the following line to the application:
using System.Drawing.Printing;
Alternatively, we can use the System.Drawing.Printing namespace by adding it to the classes directly.
Note
Before you use any printer-related classes in your application, a printer must be installed on your machine.
GDI+: The Next-Generation Graphics Interface
Your First GDI+ Application
The Graphics Class
Working with Brushes and Pens
Colors, Fonts, and Text
Rectangles and Regions
Working with Images
Advanced Imaging
Advanced 2D Graphics
Transformation
Printing
Developing GDI+ Web Applications
GDI+ Best Practices and Performance Techniques
GDI Interoperability
Miscellaneous GDI+ Examples
Appendix A. Exception Handling in .NET