Printing Multipage Text Files
The printing techniques that you've just learned are useful for simple text documents, but they have a few important limitations. First, the method I used doesn't allow for long lines—in other words, text that extends beyond the right margin. Unlike the text box object, the PrintDocument object doesn't automatically wrap lines when they reach the edge of the paper. If you have files that don't contain carriage returns at the end of lines, you'll need to write the code that handles these long lines.
The second limitation is that the Print Text program can't print more than one page of text. Indeed, it doesn't even understand what a page of text is—the printing procedure simply sends the text to the default printer. If the text block is too long to fit on a single page, the additional text won't be printed. To handle multipage printouts, you need to create a virtual page of text called the PrintPage and then add text to it until the page is full. When the page is full, it is sent to the printer, and this process continues until there is no more text to print. At that point, the print job ends.
If fixing these two limitations sounds complicated, don't despair yet—there are a few handy mechanisms that help you create virtual text pages in Visual Basic and help you print text files with long lines and several pages of text. The first mechanism is the PrintPage event, which occurs when a page is printed. PrintPage receives an argument of the type PrintPageEventArgs, which provides you with the dimensions and characteristics of the current printer page. Another mechanism is the Graphics.MeasureString method. The MeasureString method can be used to determine how many characters and lines can fit in a rectangular area of the page. By using these mechanisms and others, it's relatively straightforward to construct procedures that process multipage print jobs.
Complete the following steps to build a program named Print File that opens text files of any length and prints them. The Print File program also demonstrates how to use the RichTextBox, PrintDialog, and OpenFileDialog controls. The RichTextBox control is a more robust version of the TextBox control you just used to display text. The PrintDialog control displays a standard Print dialog box so that you can specify various print settings. The OpenFileDialog control lets you select a text file for printing. (You used OpenFileDialog in Chapter 4, “Working with Menus, Toolbars, and Dialog Boxes.”)
Manage print requests with RichTextBox, OpenFileDialog, and PrintDialog controls
Click the Close Project command on the File menu, and then create a new project named My Print File.
A blank form appears.
Use the Button control in the Toolbox to draw two buttons in the upper-left corner of the form.
This program has a simple user interface, but the printing techniques you'll learn are easily adaptable to much more complex solutions.
Click the RichTextBox control in the Toolbox, and then draw a rich text box object that covers the bottom half of the form.
Double-click the OpenFileDialog control on the Dialogs tab to add an open file dialog object to the component tray below your form.
You'll use the open file dialog object to browse for text files on your system.
Double-click the PrintDocument control on the Printing tab to add a print document object to the component tray.
You'll use the print document object to support printing in your application.
Double-click the PrintDialog control on the Printing tab to add a print dialog object to the component tray.
You'll use the print dialog object to open a Print dialog box in your program.
Now set the following properties for the objects on your form:
Your form looks something like this:
Now add the program code necessary to open the text file and print it.
Double-click the Open button.
The btnOpen_Click event procedure appears in the Code Editor.
Scroll to the top of the form, and enter the following code:
Imports System.IO 'for FileStream class Imports System.Drawing.Printing
These library definitions make available the FileStream class and the classes for printing.
Move the cursor below the Public Class Form1 statement, and then enter the following variable declarations:
Private PrintPageSettings As New PageSettings Private StringToPrint As String Private PrintFont As New Font("Arial", 10)
These statements define important information about the pages that will be printed.
Scroll to the btnOpen_Click event procedure, and then type the following program code:
Dim FilePath As String 'Display Open dialog box and select text file OpenFileDialog1.Filter = "Text files (*.txt)|*.txt" OpenFileDialog1.ShowDialog() 'If Cancel button not selected, load FilePath variable If OpenFileDialog1.FileName <> "" Then FilePath = OpenFileDialog1.FileName Try 'Read text file and load into RichTextBox1 Dim MyFileStream As New FileStream(FilePath, FileMode.Open) RichTextBox1.LoadFile(MyFileStream, _ RichTextBoxStreamType.PlainText) MyFileStream.Close() 'Initialize string to print StringToPrint = RichTextBox1.Text 'Enable Print button btnPrint.Enabled = True Catch ex As Exception 'display error messages if they appear MessageBox.Show(ex.Message) End Try End If
When the user clicks the Open button, this event procedure displays an Open dialog box using a filter that displays only text files. When the user selects a file, the file name is assigned to a public string variable named FilePath, which is declared at the top of the event procedure. The procedure then uses a Try…Catch error handler to load the text file into the RichTextBox1 object. To facilitate the loading process, I've used the FileStream class and the Open file mode, which places the complete contents of the text file into the MyFileStream variable. Finally, the event procedure enables the Print button (btnPrint) so that the user can print the file. In short, this routine opens the file and enables the print button on the form but doesn't do any printing itself.
Now you'll add the necessary program code to display the Print dialog box and print the file by using logic that monitors the dimensions of the current text page.
Add code for the btnPrint and PrintDocument1 objects
Display the form again, and then double-click the Print button (btnPrint) to display its event procedure in the Code Editor.
Type the following program code:
Try 'Specify current page settings PrintDocument1.DefaultPageSettings = PrintPageSettings 'Specify document for print dialog box and show StringToPrint = RichTextBox1.Text PrintDialog1.Document = PrintDocument1 Dim result As DialogResult = PrintDialog1.ShowDialog() 'If click OK, print document to printer If result = DialogResult.OK Then PrintDocument1.Print() End If Catch ex As Exception 'Display error message MessageBox.Show(ex.Message) End Try
This event procedure sets the default print settings for the document and assigns the contents of the RichTextBox1 object to the StringToPrint string variable (defined at the top of the form) in case the user changes the text in the rich text box. It then opens the Print dialog box and allows the user to adjust any print settings (printer, number of copies, the print to file option, and so on). If the user clicks the OK button, the event procedure sends this print job to the printer by issuing the following statement:
Display the form again, and then double-click the PrintDocument1 object in the component tray.
Visual Studio adds the PrintPage event procedure for the PrintDocument1 object.
Type the following program code in the PrintDocument1_PrintPage event procedure:
Dim numChars As Integer Dim numLines As Integer Dim stringForPage As String Dim strFormat As New StringFormat 'Based on page setup, define drawable rectangle on page Dim rectDraw As New RectangleF( _ e.MarginBounds.Left, e.MarginBounds.Top, _ e.MarginBounds.Width, e.MarginBounds.Height) 'Define area to determine how much text can fit on a page 'Make height one line shorter to ensure text doesn't clip Dim sizeMeasure As New SizeF(e.MarginBounds.Width, _ e.MarginBounds.Height - PrintFont.GetHeight(e.Graphics)) 'When drawing long strings, break between words strFormat.Trimming = StringTrimming.Word 'Compute how many chars and lines can fit based on sizeMeasure e.Graphics.MeasureString(StringToPrint, PrintFont, _ sizeMeasure, strFormat, numChars, numLines) 'Compute string that will fit on a page stringForPage = StringToPrint.Substring(0, numChars) 'Print string on current page e.Graphics.DrawString(stringForPage, PrintFont, _ Brushes.Black, rectDraw, strFormat) 'If there is more text, indicate there are more pages If numChars < StringToPrint.Length Then 'Subtract text from string that has been printed StringToPrint = StringToPrint.Substring(numChars) e.HasMorePages = True Else e.HasMorePages = False 'All text has been printed, so restore string StringToPrint = RichTextBox1.Text End If
This event procedure handles the actual printing of the text document, and it does so by carefully defining a printing area (or printing rectangle) based on the settings in the Page Setup dialog box. Any text that fits within this area can be printed normally; text that's outside this area needs to be wrapped to the following lines, or pages, as you'd expect to happen in a standard Windows application.
The printing area is defined by the rectDraw variable, which is based on the RectangleF class. The strFormat variable and the Trimming method are used to trim strings that extend beyond the edge of the right margin. The actual text strings are printed by the DrawString method, which you've already used in this chapter. The e.HasMorePages property is used to specify whether there are additional pages to be printed. If no additional pages remain, the HasMorePage property is set to False, and the contents of the StringToPrint variable are restored to the contents of the RichTextBox1 object.
Click the Save All button on the toolbar to save your changes, and specify the c:\vb05sbs\chap17 folder as the location.
That's a lot of typing! But now you're ready to run the program and see how printing text files on multiple pages works.
Run the Print File program
Click the Start Debugging button on the toolbar.
Your program runs in the IDE. Notice that the Print button is currently disabled because you haven't selected a file yet.
Click the Open button.
The program displays an Open dialog box.
Browse to the c:\vb05sbs\chap17 folder, and then click the longfile.txt file.
Your Open dialog box looks like this:
Click Open to select the file.
Your program loads the text file into the rich text box object on the form and then enables the Print button. This file is long and has a few lines that wrap so that you can test the wide margin and multipage printing options. Your form looks like this:
Verify that your printer is on, and then click the Print button.
Visual Basic displays the Print dialog box, customized with the name and settings for your printer, as shown in the following illustration:
Many of the options in the Print dialog box are active, and you can experiment with them as you would a regular Windows application.
Click Print to print the document.
Your program submits the four-page print job to the Windows print queue. After a moment (and if your printer is ready), the printer begins printing the document. As in previous exercises, a dialog box automatically appears to show you the printing status and give you an indication of how many pages your printed document will be.
Click the Close button on the form to stop the program.
The complete Print File program is located in the c:\vb05sbs\chap17\print file folder.
You've just created a set of very versatile printing routines which can be added to any Visual Basic application that needs to print multiple pages of text!