Printing in .NET

Printing is reasonably simple using .NET. Everything revolves around the PrintDocument component. This class’s main event is PrintPage. The primary method of the PrintDocument class is, as you might have guessed, Print.

So, we’ve dragged a PrintDocument component to our form, and created a default event handler for our document by double clicking on it in the component tray. To start printing, we need to call the Print method. However, nothing will print just yet. We need to add code to the PrintPage event handler to do the printing for us.

The PrintPage event handler has a PrintPageEventArgs in its signature. We can use the instance sent to the event handler to print. The important properties of this class are Graphics and HasMorePages. Graphics is an instance of the Graphics class representing the drawing surface of the document. HasMorePages defaults to false, and can be set to true if we want to print more pages. Setting this to true will mean that the PrintPage event is fired again, so we need to keep track of which pages are printing and what to print outside of the event handler if we want to print more than one page.

The other properties of PrintPageEventArgs are also very useful. Cancel can be used to cancel the job by setting it to true. MarginBounds and PageBounds are rectangles that define where the page and margin boundaries lie. For the first example, for simplicity’s sake, we’re going to print an image on a single page:

  1. Image i = Bitmap.FromFile(@”C:\image.bmp");
  2.  
  3. private void printDocument1_PrintPage(object sender,
  4.     System.Drawing.Printing.PrintPageEventArgs e)
  5. {
  6.     e.Graphics.DrawImage(i, e.MarginBounds);
  7. }

Now when Print is called, the PrintPage event will fire triggering the above method. Given the fact that e.HasMorePages is false by default, we can leave it alone and one page only will be printed.

The other aspect of printing in .NET involves the PrinterSettings class. This class is rarely accessed directly, because there are classes provided that allow the user to configure PrinterSettings without developers having to touch it. These dialogs are PrintDialog, PrintPreviewDialog and PageSetupDialog. Each works in the same way – you declare an instance of the dialog an create it. You set the Document property to your PrintDocument instance, and you call ShowDialog. For the PrintDialog, you can check the DialogResult to see if you need to call PrintDocument.Print:

  1. PrintDialog frmPrint = new PrintDialog();
  2. frmPrint.Document = doc;
  3.  
  4. if(frmPrint.ShowDialog(this) == DialogResult.OK)
  5. {
  6.     doc.Print();
  7. }
  8.  
  9. frmPrint.Dispose();
  10. frmPrint = null;

As well as using the PrintPreviewDialog, you can use the PrintPreviewControl. This has the same functionality as the dialog, but can be embedded into a form so that you can provide access to properties that the dialog doesn’t allow, such as using anti aliasing.

Printing in .NET at first is quite a simple process, but it can get complicated quickly, especially if you have a large complex multiple page document to print. For example, I have an xml file that holds RSS feeds for an RSS reader I wrote. It’s got a simple structure like this:

  1. <rssfeeds>
  2.     <category name="Alex">
  3.         <feed name="Alex's Feed"
  4.         url="http://www.alexhumphrey.me.uk/rss.ashx" />
  5.     </category>
  6.     <category name="Sport">
  7.         <feed name="Sky Sports"
  8.         url="http://www.skysports.com/rss/0,20514,12040,00.xml" />
  9.         <feed name="Sky Sports – Man U"
  10.         url="http://www.skysports.com/rss/0,20514,11667,00.xml" />
  11.     </category>
  12. </rssfeeds>

To print out the categories with their feed URLs, I can use the following code:

  1. XmlReader xml = XmlReader.Create(@”C:\hello.xml");
  2. Font f = new Font("Trebuchet MS", 12f);
  3. float fontHeight = f.GetHeight(e.Graphics);
  4.    
  5. private void printDocument1_PrintPage(object sender,
  6.     System.Drawing.Printing.PrintPageEventArgs e)
  7. {
  8.     int lines = (int)(e.MarginBounds.Height / fontHeight);
  9.     int currentLine = 0;
  10.     StringBuilder sb = new StringBuilder();
  11.     e.HasMorePages = true;
  12.  
  13.     while (currentLine &lt; lines)
  14.     {
  15.         if (xml.EOF)
  16.         {
  17.             e.HasMorePages = false;
  18.             break;
  19.         }
  20.  
  21.         switch (xml.NodeType)
  22.         {
  23.             case XmlNodeType.Element:
  24.  
  25.                 if (xml.Name == "category")
  26.                 {
  27.                     sb.Append("Category: ");
  28.                     xml.MoveToAttribute("name");    
  29.                 }
  30.                 else
  31.                 {
  32.                     sb.Append("\tFeed: ");
  33.                     xml.MoveToAttribute("url");
  34.                 }
  35.  
  36.                 sb.Append(xml.Value);
  37.                 sb.Append(Environment.NewLine);
  38.                 currentLine++;
  39.                 xml.ReadStartElement();
  40.  
  41.                 break;
  42.  
  43.             case XmlNodeType.EndElement:
  44.  
  45.                 xml.ReadEndElement();
  46.                 break;
  47.         }
  48.     }
  49.  
  50.     e.Graphics.DrawString(
  51.         sb.ToString(), f, Brushes.Black, e.MarginBounds);
  52. }

The important bit here is the GetHeight call on the font object. This enables you to work out how many lines of text you can print to a page. Simply do e.MarginBounds.Height / f.GetHeight(e.Graphics) to get the number of lines you can print with any font.

Share and Enjoy:
  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks
  • email
  • LinkedIn
  • Technorati

Leave a Reply

Your email address will not be published. Required fields are marked *