Hour 16

Printing with Visual Basic

When designing this book, the author and editors considered writing about an add-in product included with the Professional and Enterprise Editions of Visual Basic called Crystal Reports. This lesson describes printing without the Crystal Reports generator for a couple reasons. Some readers may have the Visual Basic Standard Edition and lack the Crystal Reports feature. They would be completely left without a way to print described in this entire book. In addition, if you have enough data to justify using Crystal Reports, you probably regularly use a database management system, such as Microsoft Access, that sports much more powerful reporting tools than Visual Basic. Therefore, this lesson concentrates on the fundamental reporting tools that every Visual Basic programmer will need at some time.

This lesson describes how you can integrate the Windows printer driver into Visual Basic applications that you write. Visual Basic communicates with it so that you can send text and even graphics to the printer.

The highlights of this hour include

Introducing Printing

Surprisingly, no printer control exists. Unlike most things in Visual Basic, sending output to the printer can be a tedious process. Surprisingly, one of Visual Basic's weaknesses is also its strength: Printing requires that you send a fairly long list of instructions to your printer that describe exactly the way the output is to look. As easily as Visual Basic allows you to add and manage controls, one would have thought that the printing could be made easier.

Despite the tedium sometimes associated with printing, you will soon see that you can control every aspect of printing, including the font of individual characters that your application sends to the printer. The tedious control needed for printing provides pinpoint accuracy that lets you control all printing details.

New Term: The Windows print spooler, also called the print queue or the printer subsystem, controls all printed output in Windows.

When your application sends output to the printer, Windows intercepts those printer commands. Rather than sending output directly to the printer attached to your computer, Visual Basic actually sends printed output to the Windows print spooler.

The print spooler determines how all printed output from all Windows programs eventually appears. Therefore, when your Visual Basic application attempts to send printed output directly to the printer, the Windows print spooler intercepts those commands and might change the output before the printer ever sees it.

The Windows print spooler knows how to communicate with any printer supported by Windows. There are hundreds of different kinds of printers now recognized by Windows, and most of these printers require specialized commands. If every program that you bought had to provide support for every kind of printer that you or your users might own, programs would require even more disk space than they already do. In addition, programs would cost more because each software developer would have to spend time writing the program to produce output onto every kind of printer available.

Rather than require that every software developer support all printers, the Windows print spooler requires that every software developer support only one kind of printed output: the kind required by the Windows print spooler. If the applications that you write need to produce printed output, Visual Basic produces that output in a form required by the Windows print spooler. Figure 16.1 shows that Visual Basic applications send output directly to the Windows print spooler. The Windows print spooler then converts that output into the individual commands needed by whatever printer is attached to the system.

Figure 16.1. Windows intercepts printer output.

Suppose that you had both a laser printer and a color ink-jet printer attached to your computer. Without the Windows print spooler, you would need to provide two sets of printer commands for every Visual Basic application you write. With the Windows print spooler, you need to provide only one generic set of printed output commands. Before running the application, you can use commands available in the Windows print spooler to select one of your two printers. When you run the program, Windows will convert the Visual Basic output into commands needed by whatever printer is selected.

The Windows print spooler simplifies communication with all the various printers. Your Visual Basic application needs only to send output to the Windows print spooler no matter what kind of printer that output will eventually be directed to. The Windows print spooler knows how to communicate with all Windows-supported printers and converts your Visual Basic application's output to the chosen printer's required format.

Preparing the User for Printing

Users could be caught unaware if your application begins printing without first warning the user that the printer must be ready.

New Term: Online means the printer is ready for printing.

Always remind the user to turn on the printer, make sure that the printer has paper, and ensure that the printer is online. If the user's printer is not first turned on and ready with an ample paper supply, the user will receive a Windows print spooler error message similar to the one shown in Figure 16.2.

Figure 16.2. The printer is not ready.

The function procedure in Listing 16.1 provides you with a useful MsgBox() call that you might want to incorporate into your own programs before printing. Of course, if you use common dialog boxes, you don't have to use this message box because the Print common dialog box serves good notice that printing is about to begin.

Listing 16.1. Telling the user about an upcoming print job.


Public Function PrReady() As Boolean
   ` Make sure the user is ready to print
   Dim intIsReady As Integer
   intIsReady = MsgBox("Make sure the printer is ready", _ 
              vbCritical, "Printer Check")
   If (intIsReady = vbCancel) Then
      PrReady = False ` A Cancel press returns a False value
   Else
     PrReady = True  ` User pressed OK so return True
   End If

End Function

Figure 16.3 shows the message box presented by Listing 16.1

Figure 16.3. The user can now prepare the printer.

After the user reads the message and responds to the message box, the function's return value determines whether the user wants to see the output (assuming that the user has properly prepared the printer for printing) or cancel the printing. The return value of True or False can be checked as follows from another procedure that prints based on the user's response:

If PrReady() Then      ` If function is true...
   Call PrintRoutine   ` then print from sub
End If

Introducing the Printer Object

Visual Basic applications send all printed output to a special Visual Basic object called the Printer object. The Printer object supports several property values and methods with which you determine the look of the printed output.

The Printer keyword specifies the printer object to which your applications will direct all output. There is no Printer control on the Toolbox window. All access to the Printer object must take place using Visual Basic code.

The commands that your application sends to the Printer object are generic Windows printer commands. The Windows print spooler converts those generic commands to a specific printer's commands. Therefore, you only worry about what you want printed and let the Windows print spooler worry about how the output gets produced.

Throughout this book, when you have learned about a new object, such as the Command Button control, you have learned about the properties that relate to that object. Before using the Printer object, you should see the properties available for the Printer object so that you'll know what kinds of things you can do with printed output from within Visual Basic. All of the Printer object's properties are listed in Table 16.1.

New Term: A pixel is the smallest addressable point on the screen or printer.

Table 16.1. The Printer object's properties.
Property Description
ColorMode If 1 (or if set to the vbPRCMMonochrome named literal), output prints in monochrome (shades of white and black) even if you use a color printer. If 2 (or if set to the vbPRCMColor named literal), output prints in color.
Copies Specifies the number of copies to print.
CurrentX Holds the horizontal print column, from the upper-left corner of the page, measured either in twips or the scale defined by the ScaleMode properties.
CurrentY Holds the vertical print row, from the upper-left corner of the page, measured either in twips or the scale defined by ScaleMode properties.
DeviceName The name of the output device, such as a printer driver, to which you want to print.
DrawMode Determines the appearance of graphics that you draw on the printer.
DrawStyle Specifies the style of any graphical lines that your application draws.
DrawWidth Specifies the width of lines drawn, from 1 (the default) to 32767 pixels.
DriverName The name of the printer driver (do not specify the driver's extension).
Duplex If 1 (or if set to the named literal vbPRDPSimplex), printing will occur on one side of the page. If 2 (or if set to the named literal vbPRDPHorizontal), printing will occur on both sides (if your printer supports double-sided printing) using a horizontal page turn. If 3 (or if set to the named literal vbPRDPVertical), printing will occur on both sides (if your printer supports double-sided printing) using a vertical page turn.
FillColor Specifies the color of printed shapes. Determines the shading density for noncolor printed output.
FillStyle Contains the style pattern of printed shapes.
Font Returns a font that you can use for setting font attributes.
FontBold Contains either True or False to determine whether subsequent printed output will be boldfaced.
FontCount Specifies the current printer's number of installed fonts.
FontItalic Holds either True or False to determine whether subsequent output will be italicized.
FontName Holds the name of the current font being used for output.
Fonts Contains a table of values that act as if they were stored in a control array. Fonts(0) to Fonts (FontCount-1) holds the names of all installed fonts on the target computer.
FontSize Holds the size, in points, of the current font.
FontStrikeThru Holds either True or False to determine whether subsequent output will be printed with a strikethrough line.
FontTransparent Holds either True or False to determine whether subsequent output will be transparent.
FontUnderline Holds either True or False to determine whether subsequent output will be underlined.
ForeColor Specifies the foreground color of printed text and graphics. (The paper determines the background color.)
hDC A Windows device context handle for advanced Windows procedure calls.
Height Holds the height, in twips, of the current printed page.
Orientation If 1 (or if set to the named literal vbPRORPortrait), output prints in portrait mode (printing occurs down the page). If 2 (or if set to the named literal vbPRORLandscape), output prints in landscape mode (printing occurs across the page).
Page Contains the page number currently being printed and updated automatically by Visual Basic.
PaperBin Specifies which paper bin the print job will use. You can search the online help for the PaperBin property for several named literals you can use to specify different kinds of bins.
PaperSize Specifies the size of paper the print job will use. You can search the online help for the PaperSize property for several named literals you can use to specify different sizes of paper.
Port Specifies the printer port, such as LPT1:.
PrintQuality Determines how fine the print quality will appear. If -1 (or set to the vbPRPQDraft named literal), the printing quality is the least, but the print completes quickly. If -2 (or set to the vbPRPQLow named literal), printing occurs in a low-resolution mode. If -3 (or set to the vbPRPQMedium named literal), printing occurs in a medium resolution mode. If -4 (or set to the vbPRPQHigh named literal), printing is the slowest but the highest quality.
ScaleHeight Specifies how many ScaleMode units high each graphic will be upon output.
ScaleLeft Specifies how many ScaleMode units from the left of the page subsequent printed output appears.
ScaleMode Sets the unit of measurement for all subsequent printed output that appears.
ScaleTop Specifies how many ScaleMode units from the top of the page all subsequent printed output appears.
ScaleWidth Specifies how many ScaleMode units wide each graphic will be upon printed output.
TrackDefault If True, the specified printer changes if you change the default printer at the operating system level. If False, the specified printer remains the same during the program's operation even if the system's default printer changes during the program's execution.
TwipsPerPixelX Specifies the number of screen twips that each printer's dot (called a pixel) height consumes.
TwipsPerPixelY Specifies the number of screen twips that each printer's dot, or pixel, width consumes.
Width Holds the size of the page width (measured in twips).
Zoom Specifies the percentage at which printed output prints. A negative value scales the output down (smaller), 0 requests no scaling, and a positive value scales the output up (larger).

Table 16.1 contains lots of printer properties. Fortunately, you'll use only a few of the properties for most of your printing needs. The font-related printer properties take care of just about all of your printing jobs that are textual in nature.


NOTE: The graphics-related printer properties and methods aren't covered in this lesson. Once you master graphics in the next part of this book, you'll be more prepared to understand the graphics-related Printer object's properties. Most of the Printer object's properties are reserved for controlling extremely advanced graphics output. For typical applications, you'll rarely bother to specify any properties because the default values work well for normal reporting requirements.

Unlike most of Visual Basic's control objects, the Printer object's methods are much more important than the Printer object's property values. Table 16.2 contains a complete list of the methods supported by Visual Basic's Printer object.

Table 16.2. The Printer object's methods.
Method Description
Circle Draws a circle, an ellipse, or an arc on the printer.
EndDoc Releases the current document, in full, to the print spooler for output.
KillDoc Immediately terminates the output and deletes the current print job from the print spooler.
Line Draws lines and boxes on the page.
NewPage Sends a page break to the printed output so that subsequent output appears on the next page.
PaintPicture Draws a graphic image file on the printer.
Print Prints numeric and text data on the printer.
PSet Draws a graphical point on the printed output.
Scale Determines the scale used for measuring output.
ScaleX Converts the printer's width to ScaleMode's measurement unit.
ScaleY Converts the printer's height to ScaleMode's measurement unit.
TextHeight Determines the full height of text given in the scale set with Scale.
TextWidth Determines the full width of text given in the scale set with Scale.

By far the most widely used Printer object methods are the Print, EndDoc, and NewPage methods. Once you master these three methods, you'll rarely need to use any other methods.

The Print Method

The Printer object's Print method handles almost all printed output. Print supports several different formats. With Print, you can print messages, variables, constants, and expressions on the printer. The Print method is by far the most commonly used printing method in Visual Basic. By mastering Print, you will have mastered the single most important printing method that you can master.

Here is the format of the Print method:

[Printer.]Print [Spc(n) | Tab(n)] Expression

The format makes Print look a lot more confusing than it really is, but the portion of the Print method that appears to the right of Print takes some explanation. The next several sections explain the various options available for the Print method.

Printing Literals

The Print method easily prints string and numeric literals. To print a string or numeric literal, place the literal to the right of the Print method. The following methods send the numbers 1, 2, and 3 to the Printer object for output:

Printer.Print 1
Printer.Print 2
Printer.Print 3

When execution hits these three lines of code, Visual Basic sends 1, 2, and 3 to the Printer object with each number appearing on a subsequent line. Every Print method sends a carriage return and line feed sequence to the printer. A lone Print method on a line by itself, such as the following, sends a blank line to the printer:

Printer.Print


WARNING: Print adds a space before all positive numeric values printed on the page. The space is where an invisible plus sign appears.

The following Print method sends two lines of text to the Printer object:

Printer.Print "Visual Basic makes writing programs"
Printer.Print "for Windows easy."

When the Windows print spooler gets these two lines of output, the following appears on the printer's paper:

Visual Basic makes writing programs
for Windows easy.

Printing Variables and Controls

In addition to literals, the Print method prints the contents of variables and controls. The following initializes a string variable and an integer variable and then prints the contents of the variables on the printer:

FirstName = "Charley"
Age = 24
Printer.Print FirstName
Printer.Print Age

Here is the output produced by these Print methods:

Charley
 24


NOTE: Remember that Visual Basic won't send anything to the Printer object until the code that contains Print executes. You would insert Print methods at appropriate places in the code's procedures where printed output is required. For example, if there is a command button labeled Print Report, that command button's Click() event procedure will contain Print methods.

Printing Expressions

If you could print only individual strings, numeric constants, and variables, Print would be extremely limiting. Of course, Print is not that limited. You can combine literals, variables, and expressions to the right of Print methods to produce more complex printed output. The following Print method prints 31:

Printer.Print 25 + (3 * 2)

The expression can contain variables, controls, and constants, like this:

Printer.Print sngFactor * lblWeight.Caption + 10

If you want to send special characters to the printer, you can do that by using the Chr() function. The following expression produces a message that includes embedded quotation marks inside the printed string:

Printer.Print "She said, " & Chr(34) & "I do." & Chr(34)

When execution reaches the former Print method, this is what the print spooler routes to the printer:

She said, "I do."


NOTE: You wouldn't be able to print the quotation marks without the Chr() function. Usually, Visual Basic uses the quotation marks to determine where string literals begin and end.

Printing Multiple Values

New Term: A print zone occurs every 14 columns on the page.

When you need to print several values on one line, you can do so by separating those values with semicolons and commas. The semicolon forces subsequent values to appear right next to each other in the output. The comma forces values to appear in the next print zone.

The following two messages print on different lines:

Printer.Print "The sales were
Printer.Print 4345.67

By using the semicolon, you can force these values to print next to each other:

Printer.Print "The sales were "; 4345.67

The semicolon also acts to keep automatic carriage returns and line feeds from taking place. The following Print method ends with a trailing semicolon:

Printer.Print "The company name is ";

The trailing semicolon keeps the printer's print head at the end of the message for subsequent output. Therefore, the subsequent Print statement shown next, no matter how much later in the code the Print appears, would print its output right next to the previous Print's output:

Printer.Print lblComName.Caption   ` Finsh the line

The semicolon is nice for printing multiple values of different data types of the same line. The following Print prints all its data on the same line of output:

Printer.Print "Sales: "; curTotsales; "Region:"; intRegNum

The comma is still sometimes used to force subsequent values to print in the next print zone. The following Print prints names every 14 spaces on the printed line:

Printer.Print strDivNamel, strDivName2, strDivName3

No matter how long or short each division name is, the next division name will print in the next print zone. The previous Print might produce output similar to the following:

North        NorthEast     South

When you print lists of numbers or short strings, the comma allows you to easily align each column.

Utilizing the Fonts

Most Windows-compatible printers support a variety of fonts. The font-related properties are often useful for printing titles and other special output messages in special font sizes and styles.

You can add special effects to your printed text by setting the font modifying properties from Table 16.1. For example, the following code first puts the printer in a boldfaced, italicized, 60-point font (a print size of one full inch), and then prints a message:

Printer.FontBold = True
Printer.FontItalic = True
Printer.FontSize = 60
Printer.Print "I'm learning Visual Basic!"


WARNING: The font properties affect subsequent output. Therefore, if you print several lines of text and then change the font size, the text that you've already printed remains unaffected. Visual Basic prints only the subsequent output with the new font.

Better Spacing with Spc() and Tab()

The Print method supports the use of the embedded Spc() and Tab() functions to give you additional control over your program's output. Spc() produces a variable number of spaces in the output as determined by the argument you send to Spc(). The following Print method prints a total of 10 spaces between the first name and the last:

Printer.Print strFirstName; Spc(10), strLastName

The argument that you send to the embedded Tab() function determines in which column the next printed character appears. In the following Print, the date appears in the 50th column on the page:

Printer.Print Tab(50), dteDateGenerated

As these examples show, if you print values before or after the Spc() and Tab() functions, you separate the functions from the surrounding printed values using the semicolon.


TIP: Spc() and Tab() give you more control over spacing than the comma and semicolon allow.

Listing 16.2 contains some code that computes and prints two housing pricing taxation values.

Listing 16.2. Using Spc() and Tab().


Taxl = TaxRate * HouseVal1
Tax2 = TaxRate * HouseVal2

TotalVal = HouseVal1 + HouseVal2
TotTaxes = TaxRate * TotalVal

Printer.Print "House Value"; Tab(20); "Tax"
Printer.Print Format(HouseVal1, "Currency");
Printer.Print Tab(20); Format(Taxl, "Currency")
Printer.Print Format(HouseVal2, "Currency");
Printer.Print Tab(20); Format(Tax2, "Currency")

Printer.Print  ` Prints a blank line
Printer.Print "Total tax:"; Spc(5); Format(TotTaxes, "Currency")
Printer.NewPage

Printer.EndDoc

Here is a sample of what you may see after Listing 16.2 executes:

House Value     Tax
$76,578.23      $9,189.39
$102,123.67     $12,254.81

Total tax:      $21,444.20

The Tab(20) function call ensures that the second column, which contains the tax information, is aligned. Also, notice that the trailing semicolons let you continue the Print methods on subsequent lines without squeezing long Print method values onto the same line. The code uses Spc() to insert five spaces between the title and the total amount of tax. The last two lines ensure that the printing stops properly.

Starting to Print

The physical printing doesn't begin until all output is released to the print spooler, or until your application issues the EndDoc method.

As you send Print methods to the print spooler via the Printer object, the print spooler builds the page or pages of output but doesn't release that output until you issue an EndDoc method. EndDoc tells the print spooler, "I'm done sending output to you; you can print now."

Without EndDoc, Windows would collect all of an application's output and not print any of the output until the application terminates. If you were to write an application that the user runs throughout the day and that prints invoices as customers make purchases, you would need to issue an EndDoc method at the end of each invoice-printing procedure if you wanted each invoice to print at that time.

Listing 16.3 prints a message on the printer and then signals to the print spooler that output is ready to go to paper. Without EndDoc, the print spooler would hold the output until the application containing the code terminated.

Listing 16.3. Using EndDoc to release printed output.


Printer.Print "Invoice #"; invnum
Printer.Print "Customer:"; cust(CCnt); Tab(20); "Final Sales"
Printer.Print "Amount of sale:"; Tab(20); Format(SaleAmt, "Currency")
Printer.Print "Tax:"; Tab(20); Format(tax, "Currency")
Printer.Print
Printer.Print "Total:"; Tab(20), Format(TotalSale, "Currency")

` Release the job for actual printing

Printer.EndDoc

The program containing Listing 16.3's code might continue to run and process other sets of data. The EndDoc method ensures that the output built in the preceding Print methods all gets sent to the physical printer immediately. If other Print methods appear later in the program, the print spooler begins building the output all over again, releasing that subsequent output only for an EndDoc procedure or when the application ends.

Page Breaks

When printing to the printer, you must be careful to print at the top of a new page when you want the output to advance one page. The NewPage method forces the printer to eject the current page and begin subsequent output on the next new page.

The Windows print spooler ensures that each printed page properly breaks at the end of a physical page. Therefore, if the printer's page length is 66 lines and you print 67 lines, the 67th line will appear at the top of the second page of output. There are times, however, when you need to print less than a full page on the printer. You can release that incomplete page for printing using the NewPage method (from Table 16.2). To use NewPage, simply apply the Newpage method to the Printer object like this:

Printer.NewPage


NOTE: Remember that you actually print to the Windows print spooler and that your application's output methods don't directly control a physical printer. Therefore, NewPage tells the print spooler to go to a new page when the print spooler gets to that location in the output.

You've got to remember that you're working with printers that support many fonts and font sizes. You can always determine, in advance, how many lines of output will fit on a single page as long as you first check the value of the following formula:

intNumLinesPerPage = Printer.Height / Printer.TextHeight("X")

As explained in Table 16.3, the Height property determines the height, in twips, of the page, or in whatever measurement value you want to use. The TextHeight property determines the full height of a printed character (including leading, which is the space directly above and below characters). TextHeight measures the height in twips if you haven't changed the scale using the ScaleMode property.

For printed reports, you'll rarely use the ScaleMode method. If you need to change the scale of measurement, however, you'll have to change the scale back to twips before calculating the number of output lines per page, like this:

Printer.ScaleMode = 1

ScaleMode accepts values defined in Table 16.3.

Table 16.3. The ScaleMode values.
Value Named Literal Description
0 vbUser A user-defined value
1 vbTwips Measured in twips (the default)
2 vbPoints Measured in points
3 vbPixels Measured in pixels (the smallest unit addressable by your printer)
4 vbCharacters Measured in characters (120x240 twips)
5 vbInches Measured in inches
6 vbMillimeters Measured in millimeters
7 vbCentimeters Measured in centimeters

Listing 16.4 contains code that prints two messages, one per page of printed output.

Listing 16.4. Moving to the top of new output pages.

Printer.Print "The Report begins on the next page..."
Printer.NewPage  ` Go to top of new page

Printer.Print "The Campaign Platform"


TIP: You can apply the Print method to your form to print directly on the form without using a control. For example, you can print a title on a form named frmAccts with this statement: frmAccts.Print Spc(20); "XYZ, Co." Although you should use controls as much as possible so that the application's code can rearrange and manage the text on the controls, you should remember to use Print whenever your form needs to hold unchanging text.

Summary

In this hour you have learned ways you can route output to your printer. Actually, you have learned here that all Visual Basic output goes to the Windows print spooler and the spooler takes care of speaking to your particular printer.

Creating printed output is not always simple. With the exception of printing program listings (which you can do by selecting File | Print from the development environment), printing data can take a while. You must take care of every line and jump to a new page when necessary.

The next hour starts a new part of your tutorial, where you'll create menus and add graphics to your applications.

Q&A

Q I use a network printer sometimes and a local printer sometimes, so what do I do to my application to print to either printer?

A
Absolutely nothing. Remember that your application sends all output to the Windows print spooler and not to a specific printer. When your user sees the Print dialog box (the dialog box you can produce with the Common Dialog Box control), your user selects the printer and from there you can select either your network or local printer. Windows then determines the best way to get your application's output to that printer.

Q What is the difference between Spc() and Tab()?

A
Both functions send spaces to the Printer object, but the functions differ in their starting position. Spc() adds spaces from the printer's current position. Tab() adds enough spaces to move the printer to that position on the line, no matter where the printer current rests. In addition, if you use a Tab() value such as Tab(20) but the printer is currently past position 20, Visual Basic adds another line and tabs to column position 20 on the new line.

Workshop

The quiz questions and exercises are provided for your further understanding. See Appendix C, "Answers," for answers.

Quiz

1. Why does Visual Basic printer output not go immediately to a printer?

2.
What happens if the printer is not online when the user prints something?

3.
What is the difference between the Printer object and the Print method?

4.
How can you specify the number of output copies to print?

5.
True or false: You can add the Printer object to the toolbox.

6.
How many spaces does a print zone contain?

7.
Why do you sometimes need to use the ASCII-based Chr() function when printing?

8.
What's the output from the following code?
Printer.Print "1";
Printer.Print "2"
9. True or false: Using Tab(14) after each variable does the same thing as putting a comma after each variable printed.
10. True or false: You can apply the Print method to a form.

Exercises

1. Write the Print method that prints a Spanish N (with a tilde) on the printer.

2.
Write a program that prints ASCII values 32 through 255 on paper when the user clicks a command button.

3.
Modify the book publisher application from Hour 15, "Visual Basic Database Basics," to print on paper the current book's title and year when the user clicks a command button labeled Print.