However, at some point you will probably also want to be able to produce a hard copy of the data, You are going to extend the CapsEditor sample so that it is able to print preview and print the document that is being edited.
Unfortunately there is not enough space to go into too much detail about printing here, so the printing functionality you will implement is very basic, Typically, when you are implementing the ability for an application to print data: you will need to add three items to the application’s main File menu:
- Page Setup, which allows the user to choose options such as which pages to print, which printer to use, and so on.
- Print Preview, which opens a new Form that displays a mock-up of what the printed copy should look like.
- Print, which prints the document.
In this case, to keep things simple, you will not implement a Page Setup menu option. Printing will only be possible using default settings. Note, however, that if you do want to implement Page Setup, Microsoft has already written a page setup dialog class for you to use System. windows. Forms . PrintDialog, You will normally want to write an event handler that displays this form and saves the settings chosen by the user.
In many ways, printing is just the same as displaying to a screen. You will be supplied with a device context (Graphics instance) and call all the usual display commands against that instance. Microsoft has written a number of classes to assist you in doing this; the two main ones that you need to use are System. Drawing. Printing. PrintDocument and System. Drawing. Printing .PrintPreviewDialog,These two classes handle the process of making sure that drawing instructions passed to a device context are handled appropriately for printing, leaving you to think about the logic of what to print where.
Some important differences exist between printing or print previewing on the one hand, and displaying to the screen on the other hand. Printers cannot scroll; instead, they turn out pages, Therefore, you will need to make sure that you find a sensible way of dividing your document into pages and draw each page as requested. Among other things, that means calculating how much of your document will fit onto a single page and, therefore, how many pages you will need and which page each part of the document needs to be written to.
Despite these complications, the process of printing is quite simple. Programmatically, the steps you need to go through look roughly like this:
- Printing – You instantiate a Print Document object and call its Print () method, This method signals the Print Page event to print the first page. Print Page takes a PrintPageEventArgs parameter, which supplies information concerning paper size and setup, as well as a Graphics object used for the drawing commands. You should therefore have written an event handler for this event, and have implemented this handler to print a page. This event handler should also set a Boolean property of the PrintPageEventArgs called Has More Pages to either true or false to indicate whether there are more pages to be printed, The Print Document, Print () method will repeatedly raise the PrintPage event until it sees that Has More pages has been set to false.
- Print Previewing – In this case, you instantiate both a Print Document object and a PrintPreviewDialog object. You attach the Print Document to the PrintPreviewDialog (using the property PrintPreviewDialog. no current ) and then call the dialog’s Show Dialog () method, This method modally displays the dialog, which turns out to be a standard Windows print preview form and which displays pages of the document Internally, the pages are displayed once again by repeatedly raising the PrintPage event until the Has More Pages property is false. There is no need to write a separate event handler for this; you can use the same-event handler as used for printing each page because the drawing code ought to be identical in both cases. (After all, whatever is print previewed ought to look identical to the printed version!)
Implementing Print and Print Preview
Now that this process has been outlined in broad strokes, in this section you see how this works in code terms, You can download the code as the PrintingCapsEdit project at www.csahrpaid.com; it consists of the CapsEditor project with the changes displayed in the following snippet.
You begin by using the Visual Studio 2008 design view to add two new items to the File menu: Print and Print Preview. You also use the properties window to name these items menuFilePrint and menuFilePrintPreview, and to set them to be disabled when the application starts up (you cannot print anything until a document has been opened!). You arrange for these menu items to be enabled by adding the following code to the main form’s LoadFile () method, which is responsible for loading a file into the CapsEditor application:
The above code is the new code added to this method. Next, you add a member field to the Forml class:
public partial class Forml : Form
private int pagesPrinted = 0;
This field will be used to indicate which page you are currently printing, You are making it a member field because you will need to remember this information between calls to the PrintPage event handler.
Next, you will find the event handlers that handle the selection of the Print or Print Preview menu options:
You have already seen the steps involved in printing, and you can see that these event handlers .are simply implementing that procedure. In both cases, you are instantiating a PrintDocument object and attaching an event handler to its PrintPage event. In the case of printing, you call PrintDocument. Print (),whereas for print previewing, you attach the PrintDocument object to a PrintPreviewDialog and call the preview dialog box object’s ShowDialog () method. The real work. to the Print Page event is done in the event handler. Here is what this handler looks like:
After declaring a couple of local variables, the first thing you do is work out how many lines of text can be displayed on one page, which will be the height of a page divided by the height of a line and rounded down, The height of the page can be obtained from the Prim:PageEventArgs .MarginBounds property, This property is a RectangleF struct that has been initialized to give the bounds of the page. The height of a line is obtained from the Fonnl.mainFont field, which is the font used for displaying the text, There is no reason here for not Using the same font for printing too, Note that for the PrintlngCapsEditor sample, the number of lines per page is always the same, so you arguably could have cached the value the first time you calculated it, However, the calculation is not too hard, and in a more sophisticated application the value might change; so it is not bad practice to recalculate it every time you print a page, You also initialize a variable called line No. This gives the zero-based index of the line of the document that will be the first line of this page, This information is important because, in principle, the pd_PrintPage () method could have been called to print any page, not just the first page, lineNo is computed as the number of lines per page times the number of pages that have so far been printed.
Next, you run through a loop, printing each line. This loop will terminate either when you find that you have printed all the lines of text in the document, or when you find that you have printed all the lines that will fit on this page, whichever condition occurs first. Finally, you check whether there is any more of the document to be printed, and set the Has More Pages property of your PrintPageEventArgs accordingly. You also increment the pages Printed field so that you know to print the correct page the next time the Print Page event handler is invoked.
One point to note about this event handler is that you do not worry about where the drawing commands are being sent. You simply use the Graphics object that was supplied with the PrintPageEventArgs, The Print Document class that Microsoft has written will internally take care of making sure that, if you are printing, the Graphics object has been hooked up to the printer; if you are print previewing, then the Graphics object has been hooked up to the print preview form on the Screen.
Finally, you need to ensure that the System. Drawing. Printing namespace is searched for type definitions:
. using System. Data;
using System. Drawing;
using System. Drawing. Printing;
using System. Text;
All that remains is to compile the project and check that the code works. Figure 33-19 shows what happens when you run CapsEdit, load a text document (as before, you have picked C# source file for the project), and select Print Preview.
In Figure 33-19,the document is scrolled to page 5 and the preview is set to display normal size, The PrintPreviewDialog has supplied quite a lot of features, as you can see by looking at the toolbar at the top of the form, The options available include printing the document, zooming in or out, and displaying two, three, four, or six pages together. These options are all fully functional, without your having to do any work. Figure 33-20 shows the result of changing the zoom to auto and clicking to display four pages (third toolbar button from the right).