Expand Your VB6 Printing Repertoire—Part II

Expand Your VB6 Printing Repertoire—Part II

n last month’s 10 Minute Solution you explored the basics of printing with VB6. This month’s solution continues that exploration by presenting basic code for printing and previewing text. In the process you’ll build the PrintText project, which is a very simple text editor with print and preview capabilities.

Although VB6’s Printer object is convenient, printing in VB has some serious limitations, such as a lack of support for controls for printing and previewing documents (a situation that was rectified in VB.NET). Generating elaborate printouts with VB6 requires a substantial programming effort; therefore most VB6 developers use third party controls for their printouts. But you don’t always want to include a large printing library to gain decent print capabilities; therefore, good printing tools have their place in every developer’s utilities collection.

You don’t have to rely on third-party controls to gain additional printer control in VB. This solution reviews the basics of printing with VB6 and then applies the basic concepts to build some practical tools and utilities.

The TextPrint Project
The main form (see Figure 1) of the TextPrint project for this month’s solution contains a TextBox control where you can type or paste text. You could easily substitute a more full-featured text editor to add features; but this simple text editor, based on the TextBox control, suffices to illustrate basic print and preview capabilities. The Printer Font button on the form lets you set the font used for both the TextBox and the printout. The Preview Text and Print Text buttons let you preview and print the text respectively. Whether you’re previewing or printing text, the program displays the Print dialog box, where you can select the printer and set the printout’s orientation.

Printing Text
The PrintText subroutine (see Listing 1 for the full code), which is the core of the application, displays the Print dialog box and then sets the page’s orientation with the following statements:
   CommonDialog1.ShowPrinter   If CommonDialog1.Orientation = cdlPortrait Then      Printer.Orientation = cdlPortrait      Landscape = False   Else      Printer.Orientation = cdlLandscape      Landscape = True   End If

Using the dialog, users can set the page orientation, which also sets the Printer object’s orientation. Changing the page orientation swaps the page’s width and height automatically, so you don’t need to add any other code to handle the printout’s orientation.

To print the text entered into the TextBox control, start by breaking the control’s contents into an array of strings. Each element of the array contains a single paragraph of text. If the text consisted of very short paragraphs, you could print each paragraph on a single line by passing an element of this array to the Print method as an argument. Usually, however, you have to manually break text into shorter strings, each of which must fit in a single line on the page.

To create readable text, each line must end on a word boundary, so that each string fits in the width between the left and right margins on the page. Overall, the code extracts each word to be printed from the complete text, calculates its width and then?if that word fits on the current line?adds it to the current line to be printed. Otherwise, it prints the current line contents and then starts a new line by setting the CurrentX and CurrentY properties of the output device. The GetNextWord() function controls how the code determines which portion of the remaining text constitutes the “next word.”

   Private Function GetNextWord(ByVal str As String, _      ByVal pos As Integer) As String      Dim nextWord As String      While pos  " "         nextWord = nextWord & Mid(str, pos, 1)         pos = pos + 1      Wend      While pos 

The GetNextWord() function extracts the next word from the string str, starting at location pos. Note that trailing punctuation and trailing spaces are considered part of the current word, and not as leading spaces for the following word. Tabs, carriage returns and other white space characters are not treated as spaces, and thus are not part of the current word.

The core of the text printing code is quite straightforward. The While loop retrieves the next word from the text and appends it to the newitem variable. The process continues until the code reaches the end of the text. If the combined length of the newitem string and the new word doesn't fit on the current line, the code prints the current value of newitem and then starts a new line, by setting the CurrentX property to the left margin. The next word, which hasn't been printed yet, is assigned to the variable newitem. Each line will contain at least one word. This simple example code assumes that the longest word in the document fits in the width of the page. If the code runs into a very long word, it will not break it into smaller segments. You should address that condition if you plan to use the code to print long words within tight margins, perhaps by adding a hyphenation routine.

The Print method sets the CurrentY property automatically, increasing it by one line (the height of the current font) each time you print something. The constant LineContSymbol is the character you may wish to print at the end of a line to indicate that it's been broken. It's a blank string for regular text, but you can set it to any symbol for program listings and other special types of printouts.

One of the parameters affecting the appearance of the text on the TextBox is the control's Alignment property. The PrintAlignedText() subroutine, shown below, accepts two arguments: the string to be printed and a constant that indicates the alignment of the string on the line (this constant is the value of the txtMain.Alignment setting). The code of the subroutine takes into consideration the specified alignment and prints the string on the page. To change the alignment of the text, set the Alignment property of the TextBox control to a different value and then run the program. For more information on left/right/center aligning a string across the page see last month's 10-Minute Solution.

   Private Sub PrintAlignedString( _      ByVal str As String, _      ByVal alignment As Integer)            Select Case alignment      Case 0         PRN.CurrentX = LeftMargin      Case 1         PRN.CurrentX = LeftMargin + _            PrintWidth - PRN.TextWidth(str)      Case 2         PRN.CurrentX = LeftMargin + _            (PrintWidth - PRN.TextWidth(str)) / 2      End Select      PRN.Print str   End Sub

Another basic printing operation is detecting an end-of-page condition. Each call to the Print method advances the vertical coordinate of the current location (CurrentY) automatically. To detect the end-of-page condition, the code adds the height of the current line to the vertical coordinate of the current position (CurrentY) and, if the sum exceeds the height of the page minus the bottom margin, you emit the current page using the NewPage method and start a new page. To start a new page, simply reset the current location (CurrentX, CurrentY) to the upper left corner of the available space on the page (in preview mode, to start a new page you would simply clear the preview window and then start drawing the next page). Whenever the code detects an end-of-page condition, it calls the ClearScreen() subroutine, which handles both previews and printouts:

   Private Sub ClearScreen()      If PRN Is Printer Then         PRN.NewPage      Else         PRN.Cls         PRN.Line (LeftMargin, TopMargin) - _            (LeftMargin + PrintWidth, _            TopMargin + PrintHeight), _            RGB(255, 255, 255), BF      End If      PRN.CurrentX = LeftMargin      PRN.CurrentY = TopMargin   End Sub 

The ClearScreen() subroutine clears the preview form in anticipation of the next page, or emits the current page and starts a new one on the printer. The subroutine's code distinguishes between printing and previewing by examining the PRN object. If the PRN object represents the preview form, the code displays a white rectangle that represents the area of the page within the margins (the useful printable area on the page). This rectangle extends from the upper left to the lower right corner of the printable band and it's drawn only on the preview form, and not on the printer. Notice that you can use the same statement to draw the appropriate rectangle regardless of the page's orientation, because it relies on the Printer object's Orientation property, which is already set .

Suggestions for Further Improvements
After displaying the page preview, the sample application uses a MessageBox to ask users whether it should display the following page. That's a primitive (and irritating) mechanism for previewing multiple pages, but I used it to avoid adding UI complexity to the sample application. You can?and should?design a much more functional interface for the preview form by adding a toolbar with Previous and Next buttons, allowing users to control how the application displays preview pages.

You could also add page-selection capabilities, letting users begin previewing or printing any page in the document. Here's a hint: You can simplify the implementation of the interface by storing the location (the offset index) for the first character of each preview page in an array or collection and use that information to generate the preview of any page in the printout. Once you know the index of the first character on each page, you can quickly and easily jump to the preview of any particular page. You could also provide users with the option to specify a range of pages to print. To expose the Print Range section of the Print dialog box set the Min and Max properties of the CommonDialog control.

For more formal documents, you will probably want to add statements that print a header and footer, just outside the top and bottom margins. These elements could include a document title, the date, a page number, or any other information you see fit. Just select a different font size and style for the two elements and experiment with their location.

This late in the programming game, you'd think it should be trivial to print straight text, but it isn't?it still takes quite a bit of code to print clearly. I think the TextBox control should support two methods for previewing and printing text, but that isn't even the case in Visual Studio .NET (the IDE for the newest version of Visual Basic). The code presented here is quite efficient?it takes less than a second to process a page. The basic limitation of the sample application is that you can't use the VB6's TextBox control to display strings that exceed 32K characters. You can replace the TextBox control with a RichTextBox control, which can hold a much larger amount of text, but if you do that you must also disable the control's formatting features, because the PrintText subroutine can't print formatted text (you can use the Print method of the RichTextBox control to print formatted text, but even the RichTextBox control can't preview document printouts). Next month, in the last part of this article series, you'll build a utility for printing tabular data, such as the items of the ListView control. This is the most complicated printing utility in the series, and will show you hot to mix fonts and styles in your printout.


Share the Post: