Platinum Edition Using Visual Basic 5

Previous chapterNext chapterContents


- 15 -
Displaying and Printing Reports

With the right formatting, the TextBox, RichTextBox, and ListBox controls are well-suited to display large amounts of information.
Print to the Immediate (or Debug) window to get values of variables and expressions.
Send text to the printer and line up information in columns using print zones and formatting functions.
Use fonts and colors to spice up your printout, add graphics, and precisely position each element of a report.
Print to a form and to a picture box to display reports on the screen.
Use the Print statement to send information to a file for storage or for transfer to another program.

A lot of your programming effort, and a lot of the material in this book, is geared toward getting information into your program and processing it. However, all the input and processing isn't much good if you can't get the results back out to the user. In this chapter, we focus on basic reporting and information display. When we use the term "reporting" we are not only referring to printed reports, but also on-screen information. The techniques discussed in this chapter can be used to produce both types of reports. You can also write report functions that are generic enough to be used for both the screen and printer.

Outputting Reports to Familiar Controls

If a paper report is not necessary, you can create fairly sophisticated-looking reports on-screen with some of Visual Basic's custom controls. In this section, we look at three controls suitable for doing this: the text box, the rich text box, and the list box.

Using the Text Box

You already know from earlier chapters that the Label and TextBox controls can display multiple lines of information in a variety of fonts and colors. If your report is mainly paragraphs of text, a TextBox control might be ideal. The MultiLine and ScrollBars properties allow the user to view more text than will fit on the screen at a time. By using the intrinsic constants vbCrLf to force a new line and vbTab to hit the tab stops, you can easily format a text box like the one in Figure 15.1.

FIG. 15.1
Simple reports can be created by using the TextBox control.

In Figure 15.1, all the formatting "logic" is in the text itself, not the text box. The way to code something like this is to fill the Text property incrementally:

Text1.Text = Text1.Text & "New Text"

However, rather than repeatedly accessing the Text property, it is more efficient to build your string in a local variable first and then assign it to the text box:

Dim stHeader As String
stHeader = "Gorman's Motorcycles" & vbCrLf
stHeader = stHeader & "1234 Fifth Street" & vbCrLf
stHeader = stHeader & "Anytown, MI 67321" & vbCrLf 
txtRpt.Text = txtRpt.Text & stHeader & vbCrLf

Note in the preceding example the use of the constant vbCrLf to insert a special new line character. In addition, the tab character (vbTab) can be used to create a column effect:

stTemp = stTemp & " 1" & vbTab & "Carburetor" & vbTab & "$100.00" & vbCrLf

If each item in a column has a different length, you might want to use spaces instead of tab characters:

`This code inserts one column
inPadSpaces = COLWIDTH - Len(stNewText)
If inPadSpaces < 0 then inPadSpaces = 1
txtRpt.Text = txtRpt.Text & stNewText & Space(inPadSpaces)

Note the use of the Space() function, which creates a string with the specified number of space characters.

Text boxes can also be assigned large strings without any formatting, like the bottom (Guarantee) section in Figure 15.1. If no special characters are included and the Multiline property is set to True, the text box simply wraps words automatically.

See "Modifying Text with a Program," Chapter 14


TIP: To make sure your columns line up properly, use a nonproportional font, such as Courier.

Using the RichTextBox Control

A big drawback of using the TextBox control is that color and font options can only be applied to the entire text box. A rich text box, on the other hand, has no such limitations. It allows sections of text to be formatted individually and includes additional options such as bulleted lists and embedded objects.

The RichTextBox control is designed to work with the Rich Text File Format (RTF). RTF is a standard file format for documents, similar to DOC or HTML. Both WordPad and Microsoft Word have the capability to read and write RTF files. As a matter of fact, this feature is also built in to the RichTextBox control with the LoadFile and SaveFile methods:

Private Sub cmdLoad_Click()
  Rtb1.LoadFile "C:\MYFILE.RTF"
End Sub
Private Sub cmdSave_Click()
  Rtb1.SaveFile "C:\MYFILE.RTF"
End Sub

In addition to loading and saving files, the text in a rich text box can also be manipulated with code. You probably can already imagine some useful applications. For example, you could generate reports from a database, save them to RTF format, and e-mail them to your users-- all from within a Visual Basic program.

The real strength of a RichTextBox control is in the formatting, which enables you programmatically to create sharp-looking documents like the one shown in Figure 15.2.

If you browse through the RichTextBox control's properties, you'll notice options for underlining, color, and font characteristics. The Sel prefix on many of them indicates that they affect only the selected (or highlighted) text. For example, the following code makes the selected text bold:

RichTextBox1.SelBold = True

FIG. 15.2
A rich text box allows you to use multiple formatting options within the same text box.

As with the text box, you can assign text using the Text property. Every time you set this property, the RichTextBox converts it to RTF format. You can view the RTF codes (similar to viewing HTML source) in the TextRTF property or by opening an RTF file with a text editor.

Let's look at the code used to create the report in Figure 15.2. First, I added all the information using the Text property:

Dim s As String
s = "Investment Options Report" & vbCrLf & vbCrLf
s = s & "Client" & vbCrLf & "N. D. Lazenby" & vbCrLf & vbCrLf
s = s & "Categories" & vbCrLf
s = s & "Retirement plan" & vbCrLf
s = s & "Stock options" & vbCrLf
s = s & vbCrLf & "Profits" & vbCrLf
rtb1.Text = s

Next, I formatted the first line, so I selected it with the SelStart and SelLength properties:

rtb1.SelStart = 0
rtb1.SelLength = Len("Investment Options Report")
rtb1.SelFontSize = 12
rtb1.SelItalic = True
rtb1.SelBold = True

A quicker way to highlight some text is the built-in Find method:

rtb1.Find "Client", 0
rtb1.SelUnderline = True
rtb1.SelFontSize = 11

When used as a function, Find returns the search string's starting position:

Dim nPos1 As Integer, nPos2 As Integer
nPos1 = rtb1.Find("retire", 0)
nPos2 = rtb1.Find("options", 20) + Len("options")
rtb1.SelStart = nPos1
rtb1.SelLength = nPos2 - nPos1
rtb1.SelBullet = True
rtb1.SelBold = False
rtb1.SelIndent = 200

Rich Text boxes also support embedded OLE objects, such as bitmaps:

Dim obj as OLEObject
Set obj = rtb1.OLEObjects.Add (, , "C:\graph.bmp","Paint.Picture")_

Using the ListBox Control

Another way to present lists of information is to use the ListBox control. This control enables you to add items to a list; the user can then scroll through them. While list boxes are used primarily for presenting choices to the user and allowing him to pick one or more items, they can also be used for simple reports. Listing 15.1 shows how a list box might be used to display a list of students and grades.

Listing 15.1 LISTREPORT.FRM--Using a List Control for Simple Tables

'This code assumes the students and grades
'have already been stored in variable arrays
Dim I As Integer
Dim inPadSpaces As Integer
Const NUMSTUDENTS = 10
Const COLWIDTH = 20
lstGrades.Font.Name = "Courier New" `Non-proportional font
lstGrades.Clear
For I = 1 To NUMSTUDENTS
    inPadSpaces = COLWIDTH - Len(Student(I))
    If inPadSpaces < 0 Then nPadSpaces = 1
    lstGrades.AddItem Student(I) & Space(inPadSpaces) & CStr(Grade(I))
Next I

Figure 15.3 shows what a list box would look like after executing the code in Listing 15.1.


NOTE: Although you cannot tell from looking at it, the student names in Figure 15.3 were not added in alphabetical order. Setting a list box's Sorted property to True at design time keeps your list sorted as you add items to it.

FIG. 15.3
Tabular reports can be created in a list box.

The list box has the added benefit of knowing if the user has selected an item. The items of a list box are stored in its List array, and the selected item's array index is stored in the ListIndex property. You can determine which item is selected by placing some code in the list box's Click event:

Private Sub lstGrades_Click()
  Dim stStudent As String
  Const COLWIDTH = 20
  stStudent = lstGrades.List(lstGrades.ListIndex)
  stStudent = left$(stStudent,COLWIDTH)
  Msgbox "You clicked on " & stStudent
End Sub

Instead of just using the MsgBox statement, your example could be expanded to display more information about the selected student. Note that in the preceding code you pull the student name directly from the list. The ListBox control also has a property called ItemData that can be used to store an integer value that is not displayed--for example, student ID number.

See "Working with Lists," Chapter 9

The list box concludes the discussion of simple reports with custom controls. However, it is important to note that if you do any serious on-screen reporting, you will probably rely on a grid control more than any of the controls discussed in this chapter. There are a number of third-party grid controls available. In a later chapter, we discuss the Flex Grid, which is included with Visual Basic.

See "Using the MSFlexGrid control," Chapter 11

Printing Reports

For many tasks, it is convenient to display information on the screen; however, sometimes there is just no substitute for the printed page. Printed reports are simply more convenient to carry with you to read on the plane, in a meeting, or during the commercials in your favorite TV program. The reports you create can be simple, containing plain text and some columns of numbers; or the reports can be very elaborate, containing font and color effects, tables, and even graphs. You can create any type and complexity of report in Visual Basic using the techniques in this chapter. The key is in the layout and testing of the report. Unfortunately, Visual Basic does not ship with a visual report designer for general-purpose printing. Therefore, you must work with the old-fashioned method of "code and test." After you get a little experience, however, most printing tasks become relatively easy.


NOTE: Visual Basic ships with Crystal Reports, which is a visual report designer mainly used for database reports. This designer and its associated control are covered in Chapter 29, "Using the Visual Basic Data Control."

Printable Objects

The primary means of printing text is the Print method. An object's Print method is used just like any other method. For example, place the following line of code in a form's Click event:

Form1.Print "Hello, World!"

Every time you click the form, a new line of text appears. While we will primarily discuss printing information to paper using the Printer object, the Print method works with the following four objects in Visual Basic:

The code statements you use to create your printout work the same way no matter which of these output objects you specify. In fact, you can use a class to create a generic print routine that will work with any output object.

Printing Simple Reports

In its simplest form, the Print method specifies the object where you want to print, the Print method itself, and the information you want to have printed. For example, the following statement prints a string of text to the printer:

Printer.Print "This prints a single line of text."

Used in this way, the Print method prints a single piece of information and then advances to the next line of the printer. The information you print is not limited to text. You can print text, numbers, or dates by using the Print method. Also, as you might expect, you can specify the information to be printed using either literals or variables. The following lines show several print examples:

Dim stVariable As String, inVariable As Integer
stVariable = "This is text information"
inVariable = 500
Printer.Print "Print a single string"
Printer.Print stVariable
Printer.Print 25
Printer.Print inVariable

The Print method would be of limited usefulness if you could only print a single item at a time. In fact, you can send multiple pieces of information to the printer (or other object) using the Print method. To print more than one item, simply place multiple items in the print list, and separate each item with a comma or a semicolon. If you separate two items with a comma, the second item is printed in the next print zone. This usually places some space between the items, as each print zone is 14 characters wide. If you separate the items with a semicolon, the second item is printed immediately after the first item, with no intervening spaces.

The following code sample (see Listing 15.2) shows how separators make a difference in where information is printed. The output from this code is shown in Figure 15.4.

Listing 15.2 PRINTDEMO.FRM--Commas and Semicolons Affect the Location of a Printed Item

Dim s1 As String
Dim s2 As String
s1 = "String 1"
s2 = "String 2"
`Print s1 and s2 on the same line with no spaces
picTest.Print s1; s2 `Print a blank line picTest.Print
 `Print s1 and s2 separated by some spaces picTest.Print s1;
picTest.Print "   "; s2  `Print a blank line, then s1 and s2 in separate print zones
picTest.Print vbCrLf & s1, s2

FIG. 15.4
The separators between items in a print statement determine the amount of space between them.

You can, of course, include a lot more than two or three items in the Print method's expression list. These items can be a mix of text and numbers, as long as each is separated by a comma or semicolon. You can even mix separators within the list.

One other item of note: The Print method typically starts a new line each time it is invoked. You can change this behavior by including a comma or semicolon after the last item in the expression list. This indicates to the next print statement that it should continue on the same line. Using a comma starts the next item in the next print zone, and using a semicolon starts the next item immediately after the preceding one.


CAUTION: Because the Print method does not automatically handle word wrapping, printing a lot of items or several large items can result in printing some information off the edge of the page. See the later sections "Using TextHeight and TextWidth" and "Using Word Wrapping in Your Printouts" to learn about preventing this.

Controlling Spacing in Your Reports

Specifying only a list of information to be printed leaves much to be desired in controlling the placement and appearance of any reports you are creating. Fortunately, you can position text by embedding spaces and tabs, just like the earlier examples with the TextBox control. Two special functions that are used with the Print method are the Spc and Tab functions.

The Spc function places a specified number of spaces in the printout. This is used to create a specific amount of space between two items in the expression list. You use the Spc function by placing it in the expression list of the Print method as shown in the following line of code:

Printer.Print "First Field"; Spc(10); "Second Field"

When using the Spc function, you should always use a semicolon as the separator. Otherwise, the spaces created by the Spc function will not start immediately after the preceding item, but will start in the next print zone. This will most likely give you more space than you want.

`Not a good way to use the Spc Function
Print "First Field", Spc(10), "Second Field"

The Spc function is most useful when you deal with information of a known length (such as five characters, ten characters, and so on) and with a fixed font. However, if you are trying to create columns in a report, the Tab function is usually the better choice. The Tab function causes subsequently printed characters to start at a specific location. You use the Tab function in a manner similar to the Spc function, but instead of specifying the number of spaces, you specify the character position where you want the printing to start. The following line of code causes the second item to be printed in column 30 of the printout:

Printer.Print "First Field"; Tab(30); "Second Field"

Figure 15.5 shows the effects of using the Spc and Tab functions to create columns in a report. Notice that the report using the Tab function produces more consistent results than the Spc function.

FIG. 15.5
The Spc function inserts a specified number of spaces, while the Tab function moves to a specific position.


CAUTION: If the position specified by the Tab function is further to the left on the page than the end of the last printed item, the Tab function causes the printout to move to that character position on the next line on the page.

Using Functions to Control the Appearance of Your Text

In addition to using the Spc and Tab functions to handle the spacing of your reports, there are other functions that are extremely useful in creating reports for your programs. The String function works like the Spc function, except that you can specify any character you want to fill the spaces. The Format function lets you apply special formatting to strings, numbers, and dates to make them more readable for your users.

Using the String Function You have probably seen the Table of Contents of a book where a series of dots (a dot leader) separates the title of a chapter from the page number where it starts. If you want to do this in your reports, use the String function. The String function requires two arguments--the character to use and the number of times to repeat the character. For example, the following line of code would place a series of 20 periods on the printout after the first data item:

Printer.Print "Chapter 1"; String(20, "."); "15"

In the String function, you can either specify the literal character to use or the ASCII code of the character. The following code has the same effect as the preceding line:

Printer.Print "Chapter 1"; String(20, 46); "15"

Figure 15.6 shows an example of dot leaders embedded by using the String function.

FIG. 15.6
Use the String function to embed characters in your printout.

Another use of the String function would be in printing checks, where you want to be sure that the amount line of the check is filled to avoid tampering. In this case, the typical character to use is the asterisk (*).

Using the Format Function There are several types of information that require special handling. Two, in particular, are numbers and dates. When you tell the Print method to print a number, it prints it using the least number of digits possible. There are two problems with this. First, users might be accustomed to seeing numbers in a particular format, such as two decimal places for currency numbers. Second, if all your numbers are not the same size, it makes it difficult to line them up in columns. Similar problems apply to printed dates. For example, you may want only two digits in the year or to print the full name of the month instead of an abbreviation. The Format function makes it easy for you to apply custom formatting to any text that you want to print. You can choose from some of the built-in formats or create your own by specifying format strings.

Formatting Numbers The most common use of the Format function is to place numbers in a particular format to make them easier for the users to read. To use the Format function, you specify the input value and the format as shown in the following line of code:

Printer.Print Format(crGrossSales, "Currency")

For working with numbers, there are several named formats that you can use. Table 15.1 shows the named formats for numbers that are available with the Format function. Figure 15.7 shows the various format types for a group of numbers. In all cases, you must enclose the name of the format in double quotes.

Table 15.1 amed Formats Make It Easy to Display Numbers

Named Format Description
General Number Displays the umber with no special formatting.
Currency Displays the number with a thousands separator, and two digits to the right of the decimal.
Fixed Displays at least one digit to the left and two digits to the right of the decimal.
Standard Displays the number with the thousands separator, and at least one digit to the left and two digits to the right of the decimal.
Percent Multiplies the number by 100 and displays the number followed by the percent (%) sign.
Scientific Displays the number in standard scientific notation.
Yes/No Displays Yes for a non-zero value and No for zero.
True/False Displays True for a non-zero value and False for zero.
On/Off Displays On for a non-zero value and Off for zero.

If the named formats in Visual Basic don't meet your needs, you can define your own formats. (See Table 15.2 for the codes needed to define formats.) You specify a format by creating a "template" string indicating where the digits of the number will be placed, if thousands and decimal separators will be used, and any special characters that you want printed. For example, the following line of code prints a number with four decimal places and a thousands separator:

Printer.Print Format(TotalDistance, "##,##0.0000")

FIG. 15.7
Numbers can be displayed in many ways with the Format function.

Table 15.2 Codes for Defining Numeric Formats

Symbol Purpose Meaning
0 Digit placeholder Displays the digit or displays 0 if there is no digit in that location.
# Digit placeholder Displays the digit or displays nothing if there is no digit in that location. This causes leading and trailing zeros to be omitted.
. Decimal Separator Indicates where the decimal point will be displayed.
, Thousands Separator Indicates where the separators will be displayed.
% Percentage Indicator Indicates where a percent sign will be displayed. Also causes the number to be multiplied by 100.
E-, E+, e-, e+ Scientific Notation Using E- or e- displays a minus sign next to negative exponents, but displays no sign for positive exponents. Using E+ or e+ displays a sign for any exponent.

Formatting Dates Another form of data that is often a chore to print is dates. If you specify the Print method with a date, it is displayed in the default format for your system--typically something like 2/18/98. If you want to display the date in another manner (for example, February 18, 1998), you need to use the Format function. Table 15.3 lists some predefined date and time formats (see Figure 15.8). If they do not suit your needs, you can create a custom format. The codes for creating user-defined date and time formats are listed in Visual Basic's Help system.


TIP: To include the current date and/or time on your report, you can use the Now function:

Form1.Print Format(Now,"mm/dd/yyyy")



Table 15.3 amed Date Formats

Named Format Description
General Date Shows the date and time if the expression contains both portions. Otherwise, displays either the date or the time.
Long Date Prints the day of the week, the day of the month, the month, and the year.
Medium Date Prints the day of the month, a three letter abbreviation for the month, and the year.
Short Date Prints the day, month, and year as 3/5/97.
Long Time Prints hours, minutes, and seconds along with the A.M./P.M. indication.
Medium Time Prints hours and minutes along with A.M. or P.M.
Short Time Prints hours and minutes in military time.

FIG. 15.8
By Using named formats, you can enhance the appearance of date information.


TIP: User-defined formats can be useful for making decisions in your program. The expression

Format(stMyDate,"w")

returns a number representing the day of the week. You could use the result of the expression to skip printing reports on the weekend, for example.


Creating Special Effects in Your Reports

The Print method is fine for displaying text, but used by itself is not enough to make your reports visually pleasing. If you have used a Windows word processor, you are probably already aware of several ways to alter the appearance of printed text. For example, you can make report headings larger, bold-faced, or in a different font altogether. You may also wish to change the color of certain information to draw the reader's attention to it. In addition, you can add lines, boxes, and other graphical elements to separate different sections of the report.

All of these options are available in Visual Basic. When combined with the Print method they can turn your otherwise dull printouts into sharp-looking reports. Although there are custom reporting tools available, a careful programmer can create professional-looking output using the functions described in this section.

Working with Fonts

The first step in making reports more exciting is to use different font effects to highlight information. The Print method always uses the object's current font and font characteristics when it prints information to the printer or the screen. Therefore, to use a different font or font attribute, you must make the change to the Font object before you issue the Print method. For example, place the following code in a command button:

Private Sub Command1_Click()
  Form1.Font.Bold = True
  Form1.Print "This is one font style,"
  Form1.Font.Bold = False
  Form1.Font.Size = 14
  Form1.Print "and this is another."
End Sub

Note how the Font properties act differently here than with controls such as labels or text boxes. When used with the Print method, Font properties act in a "toggle" mode. One way to think of this is that the Print method draws text on the form based on the current Font object settings, whatever they might be. A text box, on the other hand, applies any changes to the Font object to all of the text in its Text property.

The three main output objects--the printer, the form, and the picture box--all allow you to use the various attributes of the Font object, and the attributes are controlled the same way for each object.

We cover fonts in detail in Chapter 14, "Working with Text, Fonts, and Color," but the following list shows you the available font attributes that you can use to control the appearance of text in your printouts:

See "Controlling the Appearance of Your Text," Chapter 14

These properties are changed using assignment statements in your code. For forms and picture boxes, you can change the initial font settings using the Properties dialog box while you are in design mode. For the Printer, the initial font settings are determined by the Windows Print Manager.

As an example of using different font attributes in your program, consider a report that lists gross sales per salesperson for a company. As the program is printing out the columns of names and sales figures, you could use a bold font for the salespeople who made more than a specified amount in sales and use an italic font to highlight those who were below a specified minimum. This would make the information stand out and make the report more useful. The code in Listing 15.3 shows how this would work. The results of the printout are shown in Figure 15.9.

Listing 15.3 PRINTEFFECT.FRM--Using Bold and Italic to Highlight Sales Figures

Dim I As Integer
Dim GrossSales(1 To 10) As Currency
Dim SalesPerson(1 To 10) As String
'Note: the code for filling arrays has been omitted
picOutput.Font.Name = "Times New Roman"
picOutput.Font.Size = 14
picOutput.Font.Bold = False
picOutput.Font.Italic = False
For I = 1 To 10
    If GrossSales(I) > 150000 Then picOutput.Font.Bold = True
    If GrossSales(I) < 50000 Then picOutput.Font.Italic = True
    picOutput.Print SalesPerson(I); Tab(40); 
    picOutput.Print Format(GrossSales(I), "$##0,000.00")
    picOutput.Font.Bold = False
    picOutput.Font.Italic = False
Next I

Note that you are not limited to specifying a font or attribute for an entire line. You can change fonts in the middle of a line as well. To do this, use the Print method to print part of the line, followed by a semicolon. Then change the font attributes and use the Print method a second time to print the next part of the line. For example, you could modify Listing 15.3 to highlight only the number portion and not the salesperson's name, as in the following code lines:

For I = 1 To 10
   picOutput.Print SalesPerson(I); Tab(40);
   If GrossSales(I) > 150000 Then picOutput.Font.Bold = True
   If GrossSales(I) < 50000 Then picOutput.Font.Italic = True
   PicOutput.Print Format(GrossSales(I), "$##0,000.00")
   picOutput.Font.Bold = False
   picOutput.Font.Italic = False
Next I

FIG. 15.9
Font attributes are an effective way to make your data come alive.

Adding a Splash of Color

Using fonts is one way to add emphasis to various parts of a report. Color is another way. If you are printing to a form or picture box, or if you are working with a color printer, you can change the color of the text that is being printed. To change the color of the text, you specify a value for the ForeColor property of the Printer or screen object to which you are printing. Values for the ForeColor property can be intrinsic constants like vbBlue, or numeric values. Unlike TextBox or Label controls, changing the ForeColor property affects only the text that is printed after the property change. Any text already printed remains in its original color.

If you are working with a color printer, you need to change one other property--the ColorMode property. This property determines whether the printer prints in color or monochrome. There are only two possible settings of the ColorMode property, both represented by Visual Basic constants--monochrome printing (vbPRCMMonochrome) and color (vbPRCMColor). A monochrome printer ignores the setting of the ColorMode property.

To illustrate the use of color in your printouts, let's look once again at the sales figures illustrated in Listing 15.3. This time, instead of using bold and italic font properties, we will change the text color to green for the top salespeople and red for the poor performers. This change is shown in Listing 15.4.

Listing 15.4 PRINTEFFECTS.FRM--Using Color to Enhance Your Reports on the Screen and Color Printers

Dim I As Integer
Dim CurColor As Long
picOutput.Font.Name = "Times New Roman"
picOutput.Font.Size = 14
CurColor = picOutput.ForeColor
For I = 1 To 10
    picOutput.Print SalesPerson(I); Tab(40);
    If GrossSales(I) > 150000 Then picOutput.ForeColor = vbGreen
    If GrossSales(I) < 50000 Then picOutput.ForeColor = vbRed
    picOutput.Print Format(GrossSales(I), "$##0,000.00")
    picOutput.ForeColor = CurColor
Next I

We do not show a figure with the color highlights because it would not show up well in black and white. However, you can load the PrintDemo.Vbp project from the companion CD-ROM and see how the color highlighting would work.

When you are printing on a form or picture box, you can also set the BackColor property to provide a background for your text. However, you need to set the property before you start printing any text on the screen. If you try to set the BackColor property after you have started printing, you will wipe out all the text that has already been printed.


CAUTION: The BackColor property is not supported by the Printer object; you will get an error if you try to set it. In the chapter entitled "Working With Graphics," you learn how to use the PaintPicture method, which could be used to move a section of a picture box (including background color) to the printer.

See "Adding a Splash of Color," Chapter 14

Displaying Graphics

This chapter's primary focus is printing text. However, you can also include graphics in your reports. These graphics can be simple lines that set off various areas of the report, charts that complement the text that is in the report, or even graphics images such as bitmaps or JPEG and GIF pictures.

There are several graphics methods to draw on an object. For example, the following statement would draw a diagonal red line on your form:

frmTest.Line (0,0)-(200,200),vbred

The following are the three main methods used in printing graphics on a form or the printer:


NOTE: These drawing functions are discussed in detail later in the book. There is a section about each of them in "Doing Graphics," found on the CD-ROM accompanying this book.

Positioning the Parts of Your Report

So far, we have only looked at printing information in the print zones established by the Print method itself. For many situations, you will want even more control over the placement of text and graphical objects on the screen and the printed page.

Naturally, there are properties that allow you to set the absolute position of a piece of information anywhere in your print area. Using these properties allows you great flexibility in the layout of your reports. However, with the added flexibility comes added work. You have to determine the size of the object you are printing on and the size of the elements you want to print. Then you need to make sure all the pieces fit properly on the page or screen. If you have ever arranged furniture in a room, you have some idea of the process.

Visual Basic has several functions that help you determine the size of your space and the various pieces with which you are working. Printable objects also have properties to help you specify exact placement text.

Finding an Object's Printable Area

In most instances, you probably think of the printed page as a sheet of 8.5 x 11 inch paper. However, Visual Basic doesn't typically work with dimensions in terms of inches. Instead, Visual Basic works with units of measure called twips. (Recall from Chapter 2 that there are 1440 twips in an inch.) This makes measurement of spacing a little more difficult, but much more accurate. Also, when you are determining the available space on a printed page, you must consider whether the printer is working in portrait or landscape mode, and whether the paper is some size other than the standard 8.5 x 11. (For example, many legal papers are 8.5 x 14 inches.)

See "Accessing Functions with the Toolbars," Chapter 2

If you are working with the Printer object, the dimensions of the printable space are controlled primarily by the Windows Print Manager and the printer itself. (We look at how to make changes to the printer settings in the section "Controlling the Printer" later in this chapter.) For forms and picture boxes, the printable area is controlled by the initial design of the object and any resizing that has been done by the user. Because the user can resize the form (and in some cases, other objects), you cannot assume that the initial dimensions that you set will be the ones available when your printing process starts.

The best way to determine the available space in an object is to use the ScaleHeight and ScaleWidth properties of the object. These properties determine the interior dimensions of the object to which you will be printing. These properties are illustrated for a form and picture box in Figure 15.10. (Also see the "What Is the Difference Between ScaleHeight and Height?" sidebar.)

FIG. 15.10
You can determine the interior dimensions of an object with its ScaleHeight and ScaleWidth properties.


What Is the Difference Between ScaleHeight and Height?
You might be wondering why we just don't use the Height and Width properties of the object to determine its size. The reason is that the Height and Width properties specify the external dimensions of an object. For example, on a form, the Height property would include the size of the title bar, the borders on the form, and any menu bar that you might place on the form. For some forms, the Height property can be as much as 690 twips larger than the ScaleHeight property, or almost half an inch larger. You can see how this could cause problems when determining the printable area of a form. The relation of the Width and ScaleWidth properties is the same. Width specifies the external dimensions of the object while ScaleWidth specifies the internal dimensions.

To use the ScaleHeight and ScaleWidth properties in your program, you need to retrieve their values. The easiest way to do this is to store the values of the properties in a pair of variables just before your report generation routine. This is shown in the following two lines of code:

inPicWid = picOutput.ScaleWidth
inPicHgt = picOutput.ScaleHeight

After you have established the dimensions of the object, you can use them to establish relative positions on the object. For example, the following code sample starts printing some text in the middle of the object:

picOutput.CurrentX = inPicWid *.5
picOutput.CurrentY = inPicHgt *.5
picOutput.Print "Hello!"

Note the use of the CurrentX and CurrentY properties, used to set coordinates on the picture box before printing.

Using TextHeight and TextWidth

Now that you can determine the size of an object's printable area, you need to determine the size of the text you are placing on that object. The Print method is very simple-minded. It does not check to see whether the text is wider than the area where it is to be printed. Therefore, if the text is too wide, it simply disappears off the right edge of the print area, as shown in Figure 15.11.

See "Using Word Wrapping in Your Printouts," Chapter 15

FIG. 15.11
Word wrapping would be useful here.

Each of the objects that support the Print method also has two other methods that allow you to determine the size of the text to be printed. These methods are the TextHeight and TextWidth methods. As you can probably guess, the TextHeight method tells you the vertical dimensions (in twips) of a piece of text, and the TextWidth method tells you how much of an area the text will cover horizontally. The following lines of code demonstrate the use of the two methods:

Dim sgHeight As Single
Dim sgWidth As Single
Dim stUserName As String
stUserName = "STEVE BAKER"
sgHeight = Printer.TextHeight(stUsername)
sgWidth = Printer.TextWidth(stUsername)

When using the TextHeight and TextWidth methods, you must supply a string in the form of a literal string, a string variable, or a string function. If you try to use a number or numeric variable, you get an error. Therefore, if you need to print a number, use the Str, CStr, or Format functions to convert the number to a string. You should also remember that literal strings must be enclosed within quotation marks.

See "Working with Strings and Numbers," Chapter 14

When determining the size of a piece of text, the TextHeight and TextWidth methods take into account the font used by the target of the output, as well as the font attributes such as size, bold, and italic. One use of the value returned is to adjust the font size to be sure that a piece of text fits in a specified area or to make sure the maximum possible font size is used. Listing 15.5 shows how you would check the size of the text and then reduce the font if necessary to make it fit in the available space. The results are shown in Figure 15.12.

Listing 15.5 PRINTSIZE.FRM--Using TextHeight and TextWidth to Determine Whether the Font Size Needs to Be Changed

Private Sub CmdSizetext_Click()
    
    Const MINIMUM_SIZE = 8
    Dim stInput As String
    Dim inWidth As Integer
    Dim inHeight As Integer
    Dim inFontSize As Integer
    Dim blTooBig As Boolean
    picOutput.Cls
    stInput = InputBox$("Enter some text")
    If Len(stInput) = 0 Then Exit Sub
    inFontSize = Val(InputBox$("Enter initial font size", , "12"))
    lblInitial = "Initial Size: " & inFontSize
    inWidth = picOutput.ScaleWidth
    inHeight = picOutput.ScaleHeight
    picOutput.Font.Size = inFontSize
    blTooBig = True
    Do While blTooBig And inFontSize > MINIMUM_SIZE
        If picOutput.TextWidth(stInput) < inWidth And_ 
             picOutput.TextHeight(stInput) < inHeight Then
            blTooBig = False
        Else
            inFontSize = inFontSize - 1
            picOutput.Font.Size = inFontSize
        End If
    Loop
    picOutput.Print stInput
    lblActual = "Actual size: " & inFontSize
End Sub

FIG. 15.12
The text had to be resized because it would not have fit at the requested font size.

Placing the Elements of the Report

In our examples so far, we have controlled the position of printed text by adding spaces or tabs. However, there are two properties that can set the position of a piece of text within the print area. These properties are the CurrentX and CurrentY properties. Setting these properties is like positioning your drawing pencil on the form. For text, the CurrentX and CurrentY properties define the upper-left corner of the print position. When the Print method is issued, the text is printed down and to the right of this position. In other words, if the text you are about to print had a box around it, the coordinates (CurrentX, CurrentY) would represent the upper-left corner of the box, as measured from the upper-left corner of the screen.

See "Controlling Form Size," Chapter 4

The primary use of these two properties is to set the current position before printing the next piece of text in your report. If you do not modify the values of these properties, each time the Print method is invoked, the printout will start at the far left of the next available line (unless the previous Print method ended with a semicolon or comma). You can use the CurrentX and CurrentY properties to create indented areas on your report, or to handle centering and right-justifying the text. To see how the CurrentX and CurrentY properties are used, take a look at Listing 15.6 and its resulting output in Figure 15.13.

Listing 15.6 PRINTSIZE.FRM--Using CurrentX and CurrentY to Position Text on a Page

Private Sub cmd_Center_Click()
    Dim stTest As String
    Dim inWidth As Integer
    Dim inHeight As Integer
    
    stTest = "This text is centered"
    With picOutput
        inWidth = .ScaleWidth
        inHeight = .ScaleHeight
        .Font.Name = "Times New Roman"
        .Font.Size = 14
        .Font.Bold = True
        .CurrentX = (inWidth - .TextWidth(stTest)) / 2
        .CurrentY = (inHeight - .TextHeight(stTest)) / 2
    End With
    picOutput.Print stTest
End Sub

FIG. 15.13
Center text by using CurrentX and CurrentY.

You look at more ways to handle text positioning later in the chapter in "Exploring Printer Functions You Can Create."

Controlling the Printer

In our discussions so far, we have assumed that the printer was set up and ready to print, and that it would just print whatever we sent to it. For the most part, this is correct. However, there are a number of properties and methods that are used to set up the printer and then control it while your report is being created. Some of the things you can do include the following:

This section shows you how to set up the printer and how to control it.

Setting the Properties of the Printer

Although the default settings of the printer are usually sufficient for most jobs, you do have quite a bit of control over the way the printer is set up. As with most everything else in Visual Basic, you control the printer by setting properties. These properties are part of the Printer object and can be set with simple assignment statements. Table 15.4 summarizes these properties and the effects that they have on your printouts. Keep in mind that some of the properties listed in the table (such as Duplex) do not apply to every printer.

Table 15.4 Properties that Control a Printer

Property Name Description
Copies Tells the printer how many copies of each page to make.
DeviceName Returns the name of the printer--for example, HP DeskJet 660C.
DriverName Returns the name of the printer driver--for example, HPFDJC04.
Duplex Determines whether the printout will be on one side of a page or both sides. If the printout is on both sides of the page, this property also determines whether the second side assumes a horizontal or vertical flip of the page.
FontTransparent Determines whether background text and graphics will show through text printed on the page.
Orientation Determines whether the page is in portrait or landscape mode.
Page Tells your program the current page number.
PaperBin Determines which paper bin of a printer will be used. This property is also used to tell the printer to wait for manual insertion of each page to be printed. This is very useful for handling pre-printed forms.
PaperSize Sets the size of paper to be used for the printout. This property can be set to one of a number of default paper sizes. The property can also be set to allow a user-defined paper size, in which case you must define the Height and Width properties of the page.
Port Returns the name of the printer port. Can be a local or mapped port like LPT1: or a network path like \\ myserver\myprinter.
PrintQuality Sets the printer resolution to draft, low, medium, or high quality.
Zoom Sets a percentage by which the size of the printout is scaled up or down. Setting the Zoom property to 50 would cause the report to be printed half-size.

You might be wondering how you can give the user choices of how to control the printer. Fortunately, this is a relatively easy task. Most of the properties of the printer can be set using the Printer Setup dialog box of the CommonDialog control. The properties of the CommonDialog control's Print dialog box match the properties of the Printer object. The common dialog box provides an easy way for the user to select various printer properties. The Print dialog box is shown in Figure 15.14.

See "Using Built-In Dialog Boxes," Chapter 6

FIG. 15.14
The Print dialog box lets your users select and set up a printer, as well as select options such as number of copies.

Starting a New Page

As you are sending information to the printer, at some point you will print more information than will fit on a single sheet of paper. For most printers, this is not a problem as they will automatically print multiple pages as needed. However, if you are including header information on your reports, such as the report name, date, or page number, this header information will not be automatically printed at the top of the next page. This is something that you have to control from your program.

Unfortunately, the Printer object does not have any events associated with it. Therefore, it cannot tell you when you have reached the end of a page or when a new page has started. The only way for you to determine this is by keeping up with the value of the CurrentY property of the printer. When the printer moves to a new page, the values of the CurrentX and CurrentY properties are reset to 0. However, you want to be able to determine when a page break is about to occur and handle the transition to the new page yourself.

To actually force the printer to start a new page is very easy. You simply issue the NewPage method as shown in the following line of code:

Printer.NewPage

The trick in creating proper page breaks in your report is to know when the current page is full. This is accomplished using a combination of the CurrentY and ScaleHeight properties and the TextHeight method of the printer. Take a look at Listing 15.7 to see how this is accomplished.

Listing 15.7 PRINTPAGE.FRM--Using the NewPage Method to Force Page Breaks

If Printer.CurrentY + Printer.TextHeight(stOutput) > _
       Printer.ScaleHeight Then
    Printer.NewPage
    PrintHeader `(PrintHeader is programmer-defined)
End If
Printer.Print stOutput 

This code sums up the value of the CurrentY property and the height of the next line to be printed. If the sum of these numbers exceeds the ScaleHeight of the printer, the code forces a page break and then calls the PrintHeader to print the headers at the top of the next page. This code could easily be modified to handle printing a footer at the bottom of the current page before moving to the next page. This is shown in Listing 15.8.

Listing 15.8 PRINTPAGE.FRM--Handle Footers by Triggering Page Breaks Before the End of the Page

inPageHt = Printer.ScaleHeight - inFooterHt
If Printer.CurrentY + Printer.TextHeight(stOutput) > inPageHt Then
    PrintFooter 
    Printer.NewPage
    PrintHeader 
End If
Printer.Print stOutput

Of course, you are not limited to starting new pages only when the current page is full. You might also want to start a new page when a condition of the report changes. For example, if you are printing employee summary information, you might want to start a new page for each employee. Again, you start a new page by issuing the NewPage method.

Sending the Document to the Printer

As you are sending information to the printer, what you are really doing is sending information to the buffer of the Windows Print Manager. The information is not actually sent to the printer until you tell Windows to do so. Your printout is automatically sent to the printer when you shut down your application, but this does not provide sufficient control to be useful. In fact, while the Print Manager is waiting to print your report, it might be holding up printouts from other programs.

To tell Windows to send the report to the printer, you use the printer's EndDoc method. This method has no additional arguments, and simply tells Windows that your printout is finished. You run the EndDoc method by specifying the Printer object and the method name as shown in the following line:

Printer.EndDoc

Aborting the Printout

Finally, you will from time to time need to abort a printout before it is finished or prevent it from going to the printer at all. This need might arise for several reasons, including user decisions or runtime errors in the program. In any case, the method to use for aborting a printout is the KillDoc method, which is shown in the following code line:

Printer.KillDoc

One way to implement a cancel button for a printing operation is to use a form-level Boolean variable. Create a Cancel button whose Click event procedure sets the variable to True:

Private Sub cmdCancel_Click()
    blCancel = True
End Sub

While printing, check the variable with an If statement to see if you need to use the Killdoc method.

Dim inCounter As Integer
    blCancel = False
    
    For inCounter = 1 To 1000
        frmStatus.Caption = "Printing Line " & inCounter
        Printer.Print "This is line number " & inCounter
        DoEvents
        If blCancel = True Then
            Printer.KillDoc
            Exit For
        End If
   Next inCounter
    If blCancel = False Then Printer.EndDoc

In the preceding example, the DoEvents statement is used to temporarily give control back to the Windows event handler. The reason we need to do this is to make sure that the system is handling other events, specifically cmdCancel's Click event.

Typically, when this method is invoked, the Print Manager is handling the printout in the background, and the method kills the entire printout. However, there are several print-spooling options available, so using the KillDoc method does not guarantee that no information will be sent to the printer. Even if some pages have already been printed, however, the KillDoc method can still terminate the remainder of the printout, and the printer driver will reset the printer.

Printing to a Form

While the bulk of this chapter has been aimed at creating reports on the printer, you have probably gathered that most of the methods and techniques demonstrated will also work for printing information to a form or a picture box. In fact, all the figures in the chapter have used a form or picture box to represent the printer, because it is easier to show you screen shots than to create a printer page and scan it back in for a figure.

Printing to a form is basically the same as printing to the printer. You can print text, create graphics, control the fonts of the printout, handle text centering, and anything else that you can do with the printer. The only things that we have covered for the printer that are not applicable to forms and picture boxes are the printer setup properties and the NewPage, EndDoc, and KillDoc methods covered in the previous section, "Controlling the Printer."

Clearing the Form

The key difference between printing to the printer and printing to a form is that a form has a limited amount of space and cannot create multiple pages. Although this does not prevent you from creating long reports on the screen, it does force you to use different techniques to handle more than a single page of information.

If you will be printing multi-page reports to a form or a picture box, you need to do two things. First, you need functions to allow the user to navigate within the pages of data you are printing. Second, you need to clear the object before you print (display) the next page.

For the sake of this example, our data will be a string array filled by the following code (this probably should occur during the form Load event):

Const NUM_RECORDS = 100
Dim stData(1 To NUM_RECORDS) As String
Dim inCount As Integer
For inCount = 1 To NUM_RECORDS
    stData(inCount) = "This is line " & inCount & " of a 100 line report."
Next inCount

Before starting the printout, you need to establish how much information will fit on a page (the variables should have been declared in the General Declarations section of the form):

`Calculate Line height of a line of text
inLineHeight = picOutput.TextHeight("X")
`Leave 2 lines for a footer
inPageHeight = picOutput.ScaleHeight - inLineHeight * 2
'Determine lines per page
inLinesPerPage = inPageHeight / inLineHeight

With this information, you can write a procedure to print a given page:

Sub DisplayPage(inPage As Integer)
    Dim inStartRecord As Integer
    Dim inLastRecord As Integer
    Dim inCounter As Integer
    If inPage <= 1 Then
        inStartRecord = 1
    Else
        inStartRecord = 1 + (inLinesPerPage * inPage) - inLinesPerPage
    End If
    If inStartRecord > NUM_RECORDS Then inStartRecord = NUM_RECORDS
 
    inLastRecord = inLinesPerPage * inPage
    If inLastRecord > NUM_RECORDS Then inLastRecord = NUM_RECORDS
    picOutput.Cls
    inCounter = inStartRecord
    While inCounter <= inLastRecord
         picOutput.Print stData(inCounter)
         inCounter = inCounter + 1
    Wend
    picOutput.CurrentY = picOutput.ScaleHeight - inLineHeight * 2
    picOutput.Print "Footer Line 1"
    picOutput.Print "Footer Line 2"
End Sub

The output of the sample code is seen in Figure 15.15. It is left as an exercise to the reader to write code for the Previous and Next buttons, which simply change a counter variable and call the DisplayPage procedure.

The earlier sample also showed you the form's Cls method (see the picOutput.Cls line), which is used to clear the form before printing the next page of information. It clears the form (or picture box) and resets the CurrentX and CurrentY properties to 0. This is the form's equivalent of the NewPage method.

FIG. 15.15
This sample program uses the dimensions of the picture box to determine where a page begins and ends.

Using the PrintForm Method

In addition to the reporting techniques we've discussed, you can also print the entire viewable area of the form to a printer. The viewable area is all parts of the form displayed on the screen, excluding the border, the title bar, and the menu. Printing the entire form is a very simple way to generate a report from whatever is on the form. This is accomplished with the form's PrintForm method. To invoke this method, you simply specify the form to be printed as shown in the following line of code:

frmPrintPage.PrintForm


CAUTION: For information printed to the form to show up on the printed page, the AutoRedraw property of the form must be set to True.

If you omit the name of the form, the current form is assumed. Figure 15.16 shows how a form would appear on the screen and how the printed form would look.


CAUTION: On rare occasions, a video driver conflict will not allow the PrintForm method to work. On one occasion we had the CEO's laptop disassembled to the circuit-board level before figuring this out. Based upon this personal experience, I would suggest that you should use this method of printing information sparingly.

Exploring Printer Functions You Can Create

At this point, you have seen most of the properties and methods that are used to create printouts on the screen and the printer. This section shows you how to use those methods to create procedures that handle a number of text effects for you. The procedures presented in this section are designed to be as generic as possible, to give them the widest possible range of usefulness. For example, while it would be fairly easy to create a routine to center a piece of text horizontally on a printed page, the routine presented here allows you to center the text within any region on either a printed page or screen object (form or picture box). This allows you to use the procedures almost anywhere in your code.

FIG. 15.16
The form on the left shows what you see on-screen, and the one on the right shows which parts of a form are omitted by the PrintForm method.

Aligning the Text on a Page

One of the most commonly used techniques is to align the text on the page. If you use word processors, you are probably familiar with the concept of text justification. The three main types of justification are left-justified, right-justified, and centered. These all refer to the horizontal position of the text within the line. In addition, we will look at aligning text vertically with the top or bottom of the page and at centering the text vertically in the page.

Moving to the Left Left-justifying text is the simplest way to align text. If left alone, the Print method will start at the left edge of the output object and print from left to right. The routine shown in Listing 15.9 shows the function for left-justifying text and the routine that calls the function. The call to the procedure specifies the output object, the starting CurrentX and CurrentY properties for the printout, the size of the print region, and the text to be printed.

Listing 15.9 TEXTFUNC.FRM--Setting the CurrentX Property to Left-Justify Text

  Dim txStr As String
  Dim objWid As Integer
  Dim  objHgt As Integer
  txStr = Text1.Text
  objWid = Picture1.ScaleWidth
  objHgt = Picture1.ScaleHeight
  Picture1.Cls
  LeftJustify Picture1, 5, 5, objWid, objHgt, txStr
Private Sub LeftJustify(objOut As Object, LMarg, TMarg, RgWid, RgHgt _
         As Integer, InptStr As String) 
    objOut.CurrentX = LMarg
    objOut.CurrentY = TMarg
    objOut.Print InptStr
End Sub

This same routine could be used to align text at the top of the specified region.

Moving to the Right Right-justifying text is a little trickier than handling left-justified text. To align text with the right edge of a region, you must know the size of the text and the size of the region. As with the procedure for left-justifying text, the size and position of the region are passed to the procedure to right-justify the text. Therefore, the key task involved is to determine the size of the text to be printed. As you know, this can be accomplished with the TextWidth method. Listing 15.10 shows how the right-justification is accomplished. The results of the procedure are displayed in Figure 15.17.

Listing 15.10 TEXTFUNC.FRM--Using the TextWidth Function to Determine Where to Print Right-Justified Text

Private Sub RightJustify(objOut As Object, LMarg, TMarg, RgWid, RgHgt _
       As Integer, InptStr As String)
     Dim txMarg As Integer
     txMarg = RgWid - objOut.TextWidth(InptStr) - 10
    If txMarg < 0 Then txMarg = 0
    objOut.CurrentX = LMarg + txMarg
    objOut.CurrentY = TMarg
    objOut.Print InptStr 
End Sub

FIG. 15.17
A sample program demonstrates a technique for right-justification of text.

Text can be aligned along the bottom of the object as well. This is handled by the routine in Listing 15.11, which uses the TextHeight method to determine the position of the text.

Listing 15.11 TEXTFUNC.FRM--Using TextHeight to Handle Alignment Along the Bottom of the Object

Private Sub BottomAlign(objOut As Object, LMarg, TMarg, RgWid, RgHgt _
         As Integer, InptStr As String)
     Dim txMarg As Integer
    
    txMarg = RgHgt - objOut.TextHeight(InptStr) - 10
    If txMarg < 0 Then txMarg = 0
    objOut.CurrentX = LMarg
    objOut.CurrentY = TMarg + txMarg
    objOut.Print InptStr
End Sub

Staying in the Middle The final alignment function is centering. Centering is accomplished by determining the blank space available on the line and placing an equal amount of the space on both sides of the text to be printed. This is again accomplished using the CurrentX property and the TextWidth method. Listing 15.12 shows the procedure for centering text horizontally on the page. A similar method can be used to center the text vertically on the page, or to center the text in both directions. Figure 15.18 shows the results of the centering operation.

FIG. 15.18
Center text on the page either horizontally, vertically, or both.

Only horizontal centering is shown in the listing, but all three routines are included in the sample project TextFunc.Vbp. You can load this project from the accompanying CD-ROM.

Listing 15.12 TEXTFUNC.FRM--The Custom CenterText Function

Private Sub CenterText(objOut As Object, LMarg, TMarg, RgWid, RgHgt _
         As Integer, InptStr As String)
     Dim txMarg As Integer
     txMarg = (RgWid - objOut.TextWidth(InptStr)) / 2
    If txMarg < 0 Then txMarg = 0
    objOut.CurrentX = LMarg + txMarg
    objOut.CurrentY = TMarg
    objOut.Print InptStr
 End Sub

Using Word Wrapping in Your Printouts

One of the most useful functions that you can create is word wrapping. Word wrapping breaks up a line of text (usually at a space) and places additional text on the following lines of the printout. This prevents the information in the report from being printed off the edge of the page. To handle its task, the word wrapping function performs the following steps:

1. Finds the first/next word in the text using the InStr function to look for a space

2. Adds the word to a variable containing the line to be printed

3. Determines whether the text in the variable will fit on the current line

4. Repeats Steps 1 through 3 until the maximum number of words are included for the line

5. Prints the line of text

6. Removes the printed text from the input string

7. Repeats these steps until the entire input string has been printed

The procedure to handle word wrapping is shown in Listing 15.13. Figure 15.19 shows the input text in the text box and the word wrapped text in the picture box

Listing 15.13 TEXTFUNC.FRM--Word Wrapping Ensures that Information Is Not Printed Past the End of the Line

Private Sub WordWrap(objOut As Object, LMarg, TMarg, RgWid, RgHgt _
         As Integer, InptStr As String)
     Dim StrtPos As Integer
    Dim EndPos As Integer
    Dim TxtLen As Integer
    Dim PrntLn As String
    Dim PrntIn As String
         PrntIn = InptStr
    objOut.CurrentY = TMarg
     Do
       EndPos = 0
       TxtLen = 0
       PrntLn = ""
       Do
           StrtPos = EndPos + 1
           EndPos = InStr(StrtPos, PrntIn, " ")
           PrntLn = Left(PrntIn, EndPos)
           TxtLen = objOut.TextWidth(PrntLn)
       Loop Until TxtLen > RgWid - 10 Or EndPos = 0
       If EndPos = 0 Then
           PrntLn = PrntIn
           PrntIn = ""
       Else
           PrntLn = Left(PrntIn, StrtPos - 1)
           PrntIn = LTrim(Mid(PrntIn, StrtPos))
       End If
       objOut.CurrentX = LMarg
       objOut.Print PrntLn
    Loop While Len(PrntIn) > 0
 End Sub

FIG. 15.19
Word wrapping makes your report look more professional.

As with the other procedures, the WordWrap procedure allows you to specify the starting position and print region size for the output. This gives you much greater flexibility in what you can accomplish with the procedure. By varying the starting position and region size, you can handle left, right, and double indents for your paragraphs. The results of these techniques are shown in Figures 15.20 through 15.22. The actual code is contained in the sample project.

FIG. 15.20
Left Indent is handled by changing the starting position of the word wrapping.

FIG. 15.21
Right Indent is handled by changing the print region width for the word wrapping.

FIG. 15.22
Double Indent is handled by changing both the starting position and the region width.

Creating a Table with a Display Grid

The final technique we want to show you is how to create a table. The table uses several of the functions that we just created. Specifically, it uses the CenterText function to center the table headers within each column. It also uses the WordWrap function to handle word wrapping within a cell. Finally, the table uses the graphics methods to draw the lines of the table. The example presented in Listing 15.14 shows how you would print a table of book titles, ISBN numbers, and comments from the Biblio database. The table is shown in Figure 15.23.

Listing 15.14 TEXTFUNC.FRM--Using Text Functions and Line Methods to Create a Table

   Dim txStr As String
   Dim LineY As Integer    Dim  I As Integer
   Dim  objWid As Integer 
Dim  objHgt As Integer   Dim ColLft(1 To 3) As Integer, ColWid(1 To 3) As_      		     Integer
    Printer.Font.Name = "Times New Roman"
   Printer.Font.Size = 14
   Printer.Orientation = vbPRORLandscape
   objWid = Printer.ScaleWidth - 20
   objHgt = Printer.ScaleHeight - 10
   ColLft(1) = 10
   ColWid(1) = objWid / 3
   ColLft(2) = ColLft(1) + ColWid(1)
   ColWid(2) = objWid / 3
   ColLft(3) = ColLft(2) + ColWid(2)
   ColWid(3) = objWid - ColWid(1) - ColWid(2)
   LineY = 10
   Printer.Line (ColLft(1), LineY)-(ColLft(1) + objWid, LineY)
   CenterText Printer, ColLft(1), LineY + 5, ColWid(1), objHgt, "Title"
   CenterText Printer, ColLft(2), LineY + 5, ColWid(2), objHgt, "ISBN"
   CenterText Printer, ColLft(3), LineY + 5, ColWid(3), objHgt, "Comments"
   Printer.Font.Size = 10
   For I = 1 To 2
       LineY = Printer.CurrentY + 10
       Printer.Line (ColLft(1), LineY)-(ColLft(1) + objWid, LineY)
       txStr = Data1.Recordset!Title & ""
       LeftJustify Printer, ColLft(1), LineY + 5, ColWid(1), objHgt, txStr
       t xStr = Data1.Recordset!ISBN & ""
       LeftJustify Printer, ColLft(2), LineY + 5, ColWid(2), objHgt, txStr
       txStr = Data1.Recordset!Comments & ""
       WordWrap Printer, ColLft(3), LineY + 5, ColWid(3), objHgt, txStr
       Data1.Recordset.MoveNext
  ext I
    LineY = Printer.CurrentY + 10
   Printer.Line (ColLft(1), LineY)-(ColLft(1) + objWid, LineY)
   Printer.Line (ColLft(1), 10)-(ColLft(1), LineY)
   Printer.Line (ColLft(2), 10)-(ColLft(2), LineY)
   Printer.Line (ColLft(3), 10)-(ColLft(3), LineY)
   Printer.Line (ColLft(1) + objWid, 10)-(ColLft(1) + objWid, LineY)
   Printer.EndDoc

FIG. 15.23
You can create tables using text functions.

From Here...

In this chapter, you have seen how you can display reports on the screen and print them on paper. You also saw that while the Print method is fairly simple, you can use code to create a number of text effects in your printouts. You even saw how graphics could be used to enhance your reports. To learn more about some of the topics covered in this chapter, refer to the following:


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.