So far we have discussed printing only an image or a single-page file. Printing multipage files is another important part of printing functionality that developers may need to implement when writing printer applications. Unfortunately, the .NET Framework does not keep track of page numbers for you, but it provides enough support for you to keep track of the current page, the total number of pages, the last page, and a particular page number. Basically, when printing a multipage document, you need to find out the total number of pages and print them from first to last. You can also specify a particular page number. If you are using the default Windows printing dialog, then you don't have to worry about it because you can specify the pages in the dialog, and the framework takes care of this for you.
To demonstrate how to do this, our next program produces a useful printout showing all the fonts installed on your computer. This program is a useful tool for demonstrating the calculation of how many pages to print when you're using graphical commands to print.
We will use the PrintPreview facility to display the output in case you don't have access to a printer. In this example we need to track how many fonts have been printed and how far down the page we are. If we're going to go over the end of the page, we drop out of the pd_PrintPage event handler and set ev.HasMorePages to true to indicate that we have another page to print.
To see this functionality in action, let's create a Windows application and add a menu with three menu items and a RichTextBox control to the form. The final form is shown in Figure 11.24.
Figure 11.24. A form for printing multiple pages
The Display Fonts menu displays available fonts on the machine. Before we add code to this menu, we add the following variables:
private int fontcount; private int fontposition = 1; private float ypos = 1; private PrintPreviewDialog previewDlg = null;
The code for the Display Fonts menu click is given in Listing 11.44. Here we read installed fonts on the system and display them in the rich text box. We use InstalledFontCollection to read all installed fonts on a machine. Then we use the InstalledFontCollection.Families property and make a loop to read all the font families. We also check if these families support different styles, including regular, bold, italic, and underline, and we add some text to the rich text box with the current font.
Note
See Chapter 5 for details about fonts and font collections.
Listing 11.44 Displaying fonts
private void DisplayFonts_Click_1(object sender, System.EventArgs e) { // Create InstalledFontCollection object InstalledFontCollection ifc = new InstalledFontCollection(); // Get font families FontFamily[] ffs = ifc.Families; Font f; // Make sure rich text box is empty richTextBox1.Clear(); // Read font families one by one, // set font to some text, // and add text to the text box foreach(FontFamily ff in ffs) { if (ff.IsStyleAvailable(FontStyle.Regular)) f = new Font(ff.GetName(1), 12, FontStyle.Regular); else if(ff.IsStyleAvailable(FontStyle.Bold)) f = new Font(ff.GetName(1), 12, FontStyle.Bold); else if (ff.IsStyleAvailable(FontStyle.Italic)) f = new Font(ff.GetName(1), 12, FontStyle.Italic); else f = new Font(ff.GetName(1), 12, FontStyle.Underline); richTextBox1.SelectionFont=f; richTextBox1.AppendText( ff.GetName(1)+" "); richTextBox1.SelectionFont=f; richTextBox1.AppendText( "abcdefghijklmnopqrstuvwxyz "); richTextBox1.SelectionFont=f; richTextBox1.AppendText( "ABCDEFGHIJKLMNOPQRSTUVWXYZ "); richTextBox1.AppendText( "============================== "); } }
The code for the Print Preview and Print menu items is given in Listing 11.45. This code should look familiar to you. We simply create PrintDocument and PrintPreviewDialog objects, set their properties, add a print-page event handler, and call the Print and Show methods.
Listing 11.45 The Print Preview and Print menu items
private void PrintPreviewMenuClick(object sender, System.EventArgs e) { // Create a PrintPreviewDialog object previewDlg = new PrintPreviewDialog(); // Create a PrintDocument object PrintDocument pd = new PrintDocument(); // Add print-page event handler pd.PrintPage += new PrintPageEventHandler(pd_PrintPage); // Set Document property of PrintPreviewDialog previewDlg.Document = pd; // Display dialog previewDlg.Show(); } private void PrintMenuClick(object sender, System.EventArgs e) { // Create a PrintPreviewDialog object previewDlg = new PrintPreviewDialog(); // Create a PrintDocument object PrintDocument pd = new PrintDocument(); // Add print-page event handler pd.PrintPage += new PrintPageEventHandler(pd_PrintPage); // Print pd.Print(); }
The print-page event handler, pd_PrintPage, is given in Listing 11.46. We print fonts using DrawString, and we set PrintPageEventArgs.HasMorePages to true. To make sure the text fits, we increase the y-position by 60 units.
Listing 11.46 The print-page event handler
public void pd_PrintPage(object sender, PrintPageEventArgs ev) { ypos = 1; float pageheight = ev.MarginBounds.Height; // Create a Graphics object Graphics g = ev.Graphics; // Get installed fonts InstalledFontCollection ifc = new InstalledFontCollection(); // Get font families FontFamily[] ffs = ifc.Families; // Draw string on the paper while(ypos+60 < pageheight && fontposition < ffs.GetLength(0)) { // Get the font name Font f = new Font(ffs[fontposition].GetName(0),25); // Draw string g.DrawString(ffs[fontposition].GetName(0), f, new SolidBrush(Color.Black),1,ypos); fontposition = fontposition+1; ypos = ypos + 60; } if (fontposition < ffs.GetLength(0)) { // Has more pages?? ev.HasMorePages = true; } }
That's it. If we run the program, the Print menu prints multiple pages, and the Print Preview menu shows the print preview on two pages (see Figure 11.25).
Figure 11.25. Print preview of multiple pages
As you can see, it's pretty easy to create multipage report generators. Now you can use the print options to print documents with multiple pages.
11.10.1 The DocumentName Property
If you want to display the name of the document you're printing, you can use the DocumentName property of the PrintDocument object:
pd.DocumentName="A Test Document";
The new result is shown in Figure 11.26.
Figure 11.26. Setting a document name
We have seen that using the DocumentPrintPreview class is fairly straightforward. In reality, all that's happening is that this control is passed a graphics class representing each page in a printout.
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