WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

C# Printing Functions

by Budi Kurniawan
06/24/2002

Printing text and graphics is one of the more important tasks in Windows application programming. With .NET, simple printing, such as sending a string to the printer, is easy; however, the .NET Framework lacks a ready-to-use class that encapsulates complex printing tasks. For example, some coding is required if you want to allow the user to set the printer and the page to print, change the margins, print more than one copy, etc. This article offers a brief tutorial on printing using the .NET Framework class library. It starts with printing a simple string and then proceeds with some coding for page setup and print preview. To provide printing features in your Windows application, you should be familiar with the Graphics class in the System.Drawing namespace. You can find a discussion of this class in my previous article, " System.Drawing with C#."

For printing, you use the System.Drawing.Printing namespace. The main class in this namespace is the PrintDocument class, which represents an object that sends output to the printer. The role of this class is so central that you can achieve simple and complex printing tasks by using this class alone. However, as we shall see, other classes in this and other namespaces help make coding easier.

To print text or graphics, you call the Print method of the
System.Drawing.Printing.PrintDocument class. One of the events the Print method invokes is the PrintPage event. You need to wire an event handler to the PrintPage event and write the code to send output to the printer. The event handler will receive an argument of type System.Drawing.Printing.PrintPageEventArgs, containing data related to the PrintPage event. One of the properties in PrintPageEventArgs is Graphics, from which you can obtain a System.Drawing.Graphics object. This Graphics object represents a print page. To send a string to the printer, for example, you use the Graphics class' DrawString method. Of course, you can also call other methods of the Graphics class, such as FillRectangle, DrawEllipse, etc.

To illustrate how printing is done using members of the System.Drawing.Printing namespace, I've created a simple form, shown in Example 1. This is basically a blank form without any printing capability. I will gradually add code to this class to add printing features. The code in Listing 1 is a form class with a menu containing a single menu item, fileMenuItem. This item in turn contains three submenu items: filePageSetupMenuItem, filePrintPreviewMenuItem, and filePrintMenuItem.

Listing 1: The form template for printing


using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace CSharpPrinting
{
  public class Form1 : System.Windows.Forms.Form
  {
    public Form1() 
    {
      MenuItem fileMenuItem = new MenuItem("&File");
      MenuItem filePageSetupMenuItem = new MenuItem("Page Set&up...", 
        new EventHandler(filePageSetupMenuItem_Click));
      MenuItem filePrintPreviewMenuItem = new MenuItem("Print Pre&view", 
        new EventHandler(filePrintPreviewMenuItem_Click));
      MenuItem filePrintMenuItem = new MenuItem("&Print...", 
        new EventHandler(filePrintMenuItem_Click), Shortcut.CtrlP);

      fileMenuItem.MenuItems.Add(filePageSetupMenuItem);
      fileMenuItem.MenuItems.Add(filePrintPreviewMenuItem);
      fileMenuItem.MenuItems.Add(filePrintMenuItem);

      this.Menu = new MainMenu();
      this.Menu.MenuItems.Add(fileMenuItem);
    }
    // -------------- event handlers -------------------------------------
    private void filePrintMenuItem_Click(Object sender , EventArgs e) 
    {
    }
    private void filePrintPreviewMenuItem_Click(Object sender , 
    		EventArgs e) 
    {
    }
    private void filePageSetupMenuItem_Click(Object sender , 
    		EventArgs e) 
    {
    }
    //-------------- end of event handlers ------------------------------
    [STAThread]
    static void Main() 
    {
      Application.Run(new Form1());
    }
  }
}

Note that each of the three menu items in fileMenuItem is wired with an event handler in its declaration. The event handlers for the three menu items are filePageSetupMenuItem_Click, filePrintPreviewMenuItem_Click, and filePrintMenuItem_Click, as shown in the following code, which is part of the class' constructor.


MenuItem filePageSetupMenuItem = new MenuItem("Page Set&up...", 
     new EventHandler(filePageSetupMenuItem_Click));
MenuItem filePrintPreviewMenuItem = new MenuItem("Print Pre&view", 
     new EventHandler(filePrintPreviewMenuItem_Click));
MenuItem filePrintMenuItem = new MenuItem("&Print...", 
     new EventHandler(filePrintMenuItem_Click), Shortcut.CtrlP);

As you can see from the code in Listing 1, the three event handlers are currently blank.


private void filePrintMenuItem_Click(Object sender , EventArgs e) 
{
}
private void filePrintPreviewMenuItem_Click(Object sender , EventArgs e) 
{
}
private void filePageSetupMenuItem_Click(Object sender , EventArgs e) 
{
}

Now, we're ready to add code to this template to enable printing. Here is what we will do.

  1. Add a class-level variable called printDoc of type System.Drawing.Printing.PrintDocument:
    private PrintDocument printDoc = new PrintDocument();
    
    
  2. In the class' constructor, wire the PrintPage event of printDoc with an event handler we simply call printDoc_PrintPage:
    printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage); 
    
  3. Add code to the filePrintMenuItem_Click event handler:
    
    private void filePrintMenuItem_Click(Object sender, EventArgs e)
    {
      printDoc.Print();
    }
    
    
  4. Add code to printDoc_PrintPage:
    
    private void printDoc_PrintPage(Object sender , PrintPageEventArgs e)
    {
      String textToPrint = ".NET Printing is easy";
      Font printFont = new Font("Courier New", 12);
      e.Graphics.DrawString(textToPrint, printFont, Brushes.Black, 0, 0);
    }
    
    

The resulting form is given in Listing 2.

Listing 2: The form with code that prints.


using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace CSharpPrinting
{
	public class Form1 : System.Windows.Forms.Form
	{
    private PrintDocument printDoc = new PrintDocument();

    public Form1() 
    {
      MenuItem fileMenuItem = new MenuItem("&File");
      MenuItem filePageSetupMenuItem = new MenuItem("Page 
      	Set&up...", new EventHandler(filePageSetupMenuItem_Click));
      MenuItem filePrintPreviewMenuItem = new MenuItem("Print 
      	Pre&view", new EventHandler(filePrintPreviewMenuItem_Click));
      MenuItem filePrintMenuItem = new MenuItem("&Print...", 
        new EventHandler(filePrintMenuItem_Click), Shortcut.CtrlP);

      fileMenuItem.MenuItems.Add(filePageSetupMenuItem);
      fileMenuItem.MenuItems.Add(filePrintPreviewMenuItem);
      fileMenuItem.MenuItems.Add(filePrintMenuItem);

      this.Menu = new MainMenu();
      this.Menu.MenuItems.Add(fileMenuItem);
      printDoc.PrintPage += new PrintPageEventHandler(
      		printDoc_PrintPage); 
    }
    
    // -------------- event handlers ---------------------------------
    private void filePrintMenuItem_Click(Object sender , 
    		EventArgs e) 
    {
      printDoc.Print();
    }
    
    private void filePrintPreviewMenuItem_Click(Object sender , 
    		EventArgs e) 
    {
    }
    
    private void filePageSetupMenuItem_Click(Object sender , 
    		EventArgs e) 
    {
    }
    
    private void printDoc_PrintPage(Object sender , 
    		PrintPageEventArgs e)
    {
      String textToPrint = ".NET Printing is easy";
      Font printFont = new Font("Courier New", 12);
      e.Graphics.DrawString(textToPrint, printFont, 
      		Brushes.Black, 0, 0);
    }
//-------------- end of event handlers -------------------------------


[STAThread]
    static void Main() 
    {
      Application.Run(new Form1());
    }
  }
}

Now, if you run the form and press Ctrl+P (of course, assuming a printer is connected to your computer and the correct driver is installed), the printer will print the string ".NET printing is easy." Isn't it easy?

Using PrintDialog

Usually in a Windows application, you will see the Print dialog box prior to printing. This dialog box gives the user the chance to cancel printing, change the printer properties, select the number of copies, select the pages to print, etc. If you modify the filePrintMenuItem_Click in Listing 2 with the one in Listing 3, a Print dialog box will be displayed prior to printing. A Print dialog box is represented by the System.Windows.Forms.PrintDialog class.

Listing 3: Using PrintDialog


private void filePrintMenuItem_Click(Object sender , EventArgs e) 
    {
      PrintDialog dlg = new PrintDialog();
      dlg.Document = printDoc;
      if (dlg.ShowDialog() == DialogResult.OK) 
      {
        printDoc.Print();
      }
    }

The filePrintMenuItem_Click event handler in Listing 3 only sends output to the printer if the user clicks the Print dialog box's OK button. However, the settings a user changes in the Print dialog box won't take effect until you write the code to take those options into account.

Page Setup

The following section adds the feature to set up the page that is used for printing. For this, you must perform the following steps.

  1. Construct an instance of the System.Drawing.Printing.PageSettings class (for this example, I add the following to the Declarations part of the form class):
    private PageSettings pgSettings = new PageSettings();
    
  2. Set pgSettings to the DefaultPageSettings property of printDoc before printing. Therefore, add the highlighted code to the filePrintMenuItem_Click event handler in your form:
    
    private void filePrintMenuItem_Click(Object sender , EventArgs e) 
        {
          printDoc.DefaultPageSettings = pgSettings;
          PrintDialog dlg = new PrintDialog();
          dlg.Document = printDoc;
          if (dlg.ShowDialog() == DialogResult.OK) 
          {
            printDoc.Print();
          }
        }
    
    
  3. Allow the user to change the setting of the page. For our example, add the following code to the filePageSetupMenuItem_Click event handler:
    
    private void filePageSetupMenuItem_Click(Object sender , EventArgs e) 
        {
          PageSetupDialog pageSetupDialog = new PageSetupDialog();
          pageSetupDialog.PageSettings = pgSettings;
          pageSetupDialog.AllowOrientation = true;
          pageSetupDialog.AllowMargins = true;
          pageSetupDialog.ShowDialog();
        }
    
    
  4. Modify the printDoc_PrintPage event handler to take into account the top and left margins. Here is the code for our form:
    
    private void printDoc_PrintPage(Object sender , PrintPageEventArgs e)
        {
          String textToPrint = ".NET Printing is easy";
          Font printFont = new Font("Courier New", 12);
          int leftMargin = e.MarginBounds.Left;
          int topMargin = e.MarginBounds.Top;
          e.Graphics.DrawString(textToPrint, printFont, Brushes.Black, 
          		leftMargin, topMargin);
        }
    
    

Now you can select Page Setup from the File menu. What it looks like depends on your printer type. The complete code for the form that includes page setup is given in Listing 4.

Listing 4: The form class that allows the user to change the page setup


using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace CSharpPrinting
{
	public class Form1 : System.Windows.Forms.Form
	{
    private PrintDocument printDoc = new PrintDocument();
    private PageSettings pgSettings = new PageSettings();

    public Form1() 
    {
      MenuItem fileMenuItem = new MenuItem("&File");
      MenuItem filePageSetupMenuItem = new MenuItem(
      	"Page Set&up...", new EventHandler(
      		filePageSetupMenuItem_Click));
      MenuItem filePrintPreviewMenuItem = new MenuItem(
      	"Print Pre&view", new EventHandler(
      		filePrintPreviewMenuItem_Click));
      MenuItem filePrintMenuItem = new MenuItem("&Print...", 
        new EventHandler(filePrintMenuItem_Click), 
        	Shortcut.CtrlP);

      fileMenuItem.MenuItems.Add(filePageSetupMenuItem);
      fileMenuItem.MenuItems.Add(filePrintPreviewMenuItem);
      fileMenuItem.MenuItems.Add(filePrintMenuItem);

      this.Menu = new MainMenu();
      this.Menu.MenuItems.Add(fileMenuItem);
      printDoc.PrintPage += new PrintPageEventHandler(
      		printDoc_PrintPage); 
    }
    
    
    // -------------- event handlers ----------------------
    private void filePrintMenuItem_Click(Object sender , 
    		EventArgs e) 
    {


      printDoc.DefaultPageSettings = pgSettings;
      PrintDialog dlg = new PrintDialog();
      dlg.Document = printDoc;
      if (dlg.ShowDialog() == DialogResult.OK) 
      {
        printDoc.Print();
      }
    }
    
    private void filePrintPreviewMenuItem_Click(Object sender , 
    		EventArgs e) 
    {
    }
    
    private void filePageSetupMenuItem_Click(Object sender , 
    		EventArgs e) 
    {
      PageSetupDialog pageSetupDialog = new PageSetupDialog();
      pageSetupDialog.PageSettings = pgSettings;
      pageSetupDialog.AllowOrientation = true;
      pageSetupDialog.AllowMargins = true;
      pageSetupDialog.ShowDialog();
    }
    
     
    private void printDoc_PrintPage(Object sender , 
    		PrintPageEventArgs e)
    {
      String textToPrint = ".NET Printing is easy";
      Font printFont = new Font("Courier New", 12);
      int leftMargin = e.MarginBounds.Left;
      int topMargin = e.MarginBounds.Top;
      e.Graphics.DrawString(textToPrint, printFont, 
      	Brushes.Black, leftMargin, topMargin);
    }
    

    //-------------- end of event handlers ------------------

		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

	}
}

Printer Setting

You can also enable the user to change printer settings by performing the following steps.

  1. Create an instance of the System.Drawing.Printing.PrinterSettings class. For our form, add the following line of code:
    private PrinterSettings prtSettings = new PrinterSettings();
    
  2. Set prtSettings to the PrinterSettings property of the PageSetupDialog:
    
    private void filePageSetupMenuItem_Click(Object sender , EventArgs e) 
        {
          PageSetupDialog pageSetupDialog = new PageSetupDialog();
          pageSetupDialog.PageSettings = pgSettings;
          pageSetupDialog.PrinterSettings = prtSettings;
          pageSetupDialog.AllowOrientation = true;
          pageSetupDialog.AllowMargins = true;
          pageSetupDialog.ShowDialog();
        }
    

Now the Printer button in the Page Setup dialog is enabled. If you click the Printer button, the Printer setting page will be displayed.

Print Preview

In a Windows application, before the user prints, they can normally view a preview of how the printout will like on paper. You can also provide this feature by adding the following code to the filePrintPreviewMenuItem_Click event handler.


private void filePrintPreviewMenuItem_Click(Object sender , EventArgs e) 
    {
      PrintPreviewDialog dlg = new PrintPreviewDialog();
      dlg.Document = printDoc;
      dlg.ShowDialog();
    }

The Print Preview dialog box is represented by the System.Windows.Forms.PrintPreviewDialog class. You can create an instance of this dialog box by using its no-argument constructor. Then you must assign the PrintDocument object to print to the Document property of the PrintPreviewDialog object. When the ShowDialog method is called, it will invoke the PrintPage event of the PrintDocument object. However, the output will not be sent to the printer but to the PrintPreviewDialog object. The complete code is given in Listing 5.

Listing 5: The complete code with Page Setup and Print Preview


using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace CSharpPrinting
{
	public class Form1 : System.Windows.Forms.Form
	{
    private PrintDocument printDoc = new PrintDocument();
    private PageSettings pgSettings = new PageSettings();
    private PrinterSettings prtSettings = new PrinterSettings();

    public Form1() 
    {
      MenuItem fileMenuItem = new MenuItem("&File");
      MenuItem filePageSetupMenuItem = new MenuItem("Page Set&up...", 
        new EventHandler(filePageSetupMenuItem_Click));
      MenuItem filePrintPreviewMenuItem = new MenuItem("Print Pre&view", 
        new EventHandler(filePrintPreviewMenuItem_Click));
      MenuItem filePrintMenuItem = new MenuItem("&Print...", 
        new EventHandler(filePrintMenuItem_Click), Shortcut.CtrlP);

      fileMenuItem.MenuItems.Add(filePageSetupMenuItem);
      fileMenuItem.MenuItems.Add(filePrintPreviewMenuItem);
      fileMenuItem.MenuItems.Add(filePrintMenuItem);

      this.Menu = new MainMenu();
      this.Menu.MenuItems.Add(fileMenuItem);
      printDoc.PrintPage += new PrintPageEventHandler(
      	printDoc_PrintPage); 
    }
        
    // -------------- event handlers ------------------------------------
    private void filePrintMenuItem_Click(Object sender , 
    	EventArgs e) 
    {

      printDoc.DefaultPageSettings = pgSettings;
      PrintDialog dlg = new PrintDialog();
      dlg.Document = printDoc;
      if (dlg.ShowDialog() == DialogResult.OK) 
      {
        printDoc.Print();
      }
    }
    
    private void filePrintPreviewMenuItem_Click(Object sender , 
    	EventArgs e) 
    {
      PrintPreviewDialog dlg = new PrintPreviewDialog();
      dlg.Document = printDoc;
      dlg.ShowDialog();
    }
    
    private void filePageSetupMenuItem_Click(Object sender , 
    	EventArgs e) 
    {
      PageSetupDialog pageSetupDialog = new PageSetupDialog();
      pageSetupDialog.PageSettings = pgSettings;
      pageSetupDialog.PrinterSettings = prtSettings;
      pageSetupDialog.AllowOrientation = true;
      pageSetupDialog.AllowMargins = true;
      pageSetupDialog.ShowDialog();
    }
    
    private void printDoc_PrintPage(Object sender , 
    	PrintPageEventArgs e)
    {
      String textToPrint = ".NET Printing is easy";
      Font printFont = new Font("Courier New", 12);
      int leftMargin = e.MarginBounds.Left;
      int topMargin = e.MarginBounds.Top;
      e.Graphics.DrawString(textToPrint, printFont, Brushes.Black, 
      		leftMargin, topMargin);
    }

    //-------------- end of event handlers -----------------------------

		[STAThread]
		static void Main() 
		{
			Application.Run(new Form1());
		}

	}
}

Conclusion

As you can see, this article has shown how to provide the printing feature in your Windows application, step-by-step. However, the code for printing is scattered around the other code. It is a good idea to encapsulate the printing feature in a class of its own so that you can have the printing feature without making your code "dirty."

Budi Kurniawan is a senior J2EE architect and author.


Return to .NET DevCenter