Graphics, Multimedia, and Printing Recipes—Recipe 8 15
Recipe 8-15. Print a Multipage Document
Problem
You need to print complex documents with multiple pages and possibly print several different documents at once.
Solution
Place the information you want to print into a custom class that derives from PrintDocument, and in the PrintPage event handler, set the PrintPageEventArgs.HasMorePages property to true as long as pages are remaining.
How It Works
The PrintDocument.PrintPage event is triggered to let you to print only a single page. If you need to print more pages, you need to set the PrintPageEventArgs.HasMorePages property to true in the PrintPage event handler. As long as HasMorePages is set to true, the PrintDocument class will continue firing PrintPage events. However, it is up to you to track which page you are on, what data should be placed on each page, and what is the last page for which HasMorePage is not set to true. To facilitate this tracking, it is a good idea to create a custom class.
The Code
The following example shows a class called TextDocument. This class inherits from PrintDocument and adds three properties. Text stores an array of text lines, PageNumber reflects the last printed page, and Offset indicates the last line that was printed from the Text array.
public class TextDocument : PrintDocument { private string[] text; private int pageNumber; private int offset; public string[] Text { get { return text; } set { text = value; } } public int PageNumber { get { return pageNumber; } set { pageNumber = value; } } public int Offset { get { return offset; } set { offset = value; } } public TextDocument(string[] text) { this.Text = text; } }
Depending on the type of material you are printing, you might want to modify this class. For example, you could store an array of image data, some content that should be used as a header or footer on each page, font information, or even the name of a file from which you want to read the information. Encapsulating the information in a single class makes it easier to print more than one document at the same time. This is especially important because the printing process runs in a new dedicated thread. As a consequence, the user is able to keep working in the application and therefore update your data while the pages are printing. So, this dedicated class should contain a copy of the data to print to avoid any concurrency problems.
The code that initiates printing is the same as in recipe 8-14, only now it creates a TextDocument instance instead of a PrintDocument instance. The PrintPage event handler keeps track of the current line and checks whether the page has space before attempting to print the next line. If a new page is needed, the HasMorePages property is set to true and the PrintPage event fires again for the next page. If not, the print operation is deemed complete. This simple code sample also takes into account whether a line fits into the width of the page; refer to recipe 8-16 for a solution to this problem.
The full form code is as follows:
using System; using System.Drawing; using System.Windows.Forms; using System.Drawing.Printing; namespace Apress.VisualCSharpRecipes.Chapter08 { public partial class Recipe08_15 : Form { public Recipe08_15() { InitializeComponent(); } private void cmdPrint_Click(object sender, EventArgs e) { // Create a document with 100 lines. string[] printText = new string[101]; for (int i = 0; i < 101; i++) { printText[i] = i.ToString(); printText[i] += ": The quick brown fox jumps over the lazy dog."; } PrintDocument doc = new TextDocument(printText); doc.PrintPage += this.Doc_PrintPage; PrintDialog dlgSettings = new PrintDialog(); dlgSettings.Document = doc; // If the user clicked OK, print the document. if (dlgSettings.ShowDialog() == DialogResult.OK) { doc.Print(); } } private void Doc_PrintPage(object sender, PrintPageEventArgs e) { // Retrieve the document that sent this event. TextDocument doc = (TextDocument)sender; // Define the font and determine the line height. using (Font font = new Font("Arial", 10)) { float lineHeight = font.GetHeight(e.Graphics); // Create variables to hold position on page. float x = e.MarginBounds.Left; float y = e.MarginBounds.Top; // Increment the page counter (to reflect the page that // is about to be printed). doc.PageNumber += 1; // Print all the information that can fit on the page. // This loop ends when the next line would go over the // margin bounds, or there are no more lines to print. while ((y + lineHeight) < e.MarginBounds.Bottom && doc.Offset <= doc.Text.GetUpperBound(0)) { e.Graphics.DrawString(doc.Text[doc.Offset], font, Brushes.Black, x, y); // Move to the next line of data. doc.Offset += 1; // Move the equivalent of one line down the page. y += lineHeight; } if (doc.Offset < doc.Text.GetUpperBound(0)) { // There is still at least one more page. // Signal this event to fire again. e.HasMorePages = true; } else { // Printing is complete. doc.Offset = 0; } } } } }
|

