Printing Multiple Pages in C#

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 pates, 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 programs 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 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 display available fonts on the machine. Before we add code to this menu, we add the following variables:
  1. private int fontcount;  
  2. private int fontposition = 1;  
  3. private float ypos = 1;  
  4. 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 wee add some text to the rich text box with the current font.
LISTING 11.44: Displaying fonts

  1. private void DisplayFonts_Click1(object sender, Sytem.EventArgs e) {  
  2.  //Create InstalledFontCollection objects  
  3.  InstalledFontCollection ifc =  
  4.   new InstalledFontCollection();  
  5.  //Get font families  
  6.  FontFamily[] ffs = ifc.Families;  
  7.  Font f;  
  8.  //Make sure rich text box is empty  
  9.  richTextBox1.Clear();  
  10.  //Read font families one by one,  
  11.  //set font to some text,  
  12.  //and add text to the text box  
  13.  foreach(FontFamily ff in ffs) {  
  14.   if (ff.IsStyleAvailable(FontStyle.Regular))  
  15.    f = new Font(ff.GetName(1), 12, FontStyle.Regular);  
  16.   else if (ff.IsStyleAvailable(FontStyle.Bold))  
  17.    f = new Font(ff.GetName(1), 12, FontStyle.Bold);  
  18.   else if (ff.IsStyleAvailable(FontStyle.Italic))  
  19.    f = new Font(ff.GetName(1), 12, FontStyle.Italic);  
  20.   else  
  21.    f = new Font(ff.GetName(1), 12, FontStyle.Underline);  
  22.   richTextBox1.SelectionFont = f;  
  23.   richTextBox1.AppendText(ff.GetName(1) + "\r\n");  
  24.   richTextBox1.SelectionFont = f;  
  25.   richTextBox1.AppendText("abcdefghijklmnopqrstuvwxyz\r\n");  
  26.   richTextBox1.SelectionFont = f;  
  27.   richTextBox1.AppendText("ABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n");  
  28.   richTextBox1.AppendText("==========================\r\n");  
  29.  }  
  30. }  
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
  1. private void PrintPreviewMenuClick(object sender, System.EventArgs e)  
  2. {  
  3.     //Create a PrintPreviewDialog object  
  4.     previewDlg = new PrintPreviewDialog();  
  5.     //Create a PrintDocument object  
  6.     PrintDocument pd = new PrintDocument();  
  7.     //Add print-page event handler  
  8.     pd.PrintPage +=  
  9.     new PrintPageEventHanlder(pd_PrintPage);  
  10.     //Set Document property of PrintPreviewDialog  
  11.     previewDlg.Document = pd;  
  12.     //Display dialog  
  13.     previewDlg.Show();  
  14. }  
  16. private void PrintMenuClick(object sender, System.EventArgs e)  
  17. {  
  18.     //Create a PrintPreviewDialog object  
  19.     previewDlg = new PrintPreviewDialog();  
  20.     //Create a PrintDocument object  
  21.     PrintDocument pd = new PrintDocument();  
  22.     //Add print-page event handler  
  23.     pd.PrintPage +=  
  24.     new PrintPageEventHandler(pd_PrintPage);  
  25.     //Print  
  26.     pd.Print();  
  27. }  


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
  1. public void pd_PrintPage(object sender, PrintPageEventArgs ev)  
  2. {  
  3.     ypos = 1;  
  4.     float pageheight = ev.MarginBounds.Height;  
  5.     //Create a Graphics object  
  6.     Graphics g = ev.Graphics;  
  7.     //Get installed fonts  
  8.     InstalledFontCollection ifc =  
  9.     new InstalledFontCollection();  
  10.     //Get font families  
  11.     FontFamily[] ffs = ifc.Families;  
  12.     //Draw string on the paper  
  13.     while (ypos + 60 < pageheight &&  
  14.     fontposition < ffs.GetLength(0))  
  15.     {  
  16.         //Get the font name   
  17.         Font f =  
  18.         new Font(ffs[fontposition].GetName(0), 25);  
  19.         //Draw string  
  20.         g.DrawString(ffs[fontposition].GetName(0), f,  
  21.         new SolidBrush(Color.Black), 1, ypos);  
  22.         fontposition = fontposition + 1;  
  23.         ypos = ypos + 60;  
  24.     }  
  25.     if (fontposition < ffs.GetLength(0))  
  26.     {  
  27.         //Has more pages??  
  28.         ev.HasMorePages = true;  
  29.     }  
  30. }  
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).
As you can see, it's pretty easy to create multipage report generators. Now you can use the print option to print documents with multiple pages.
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 Text Document";
The new result is shown in Figure 11.26.
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.
FIGURE 11.25: Print preview of multiple pages
FIGURE 11.26: Setting a document name