The first step in this process is to build some common routines that can be used to handle the differences between the Printer object and the Picture control.
The WriteNewPage routine is called before I start writing anything to the preview window or the printer, as you can see in Listing 9.13. It accepts two parameters: the PrintObject and the PageNumber. The PrintObject contains an object reference to either a Printer object or the Picture control where the output will be sent. If the object is a Printer, then I need to execute the NewPage method on every page after the first. If this object is a Picture control, then all I need to do is clear the screen.
Listing 9.13: WritePage Routine in Charter
Private Sub WriteNewPage(PrintObject As Object, PageNumber As Integer)
If TypeOf PrintObject Is Printer Then
If PageNumber > 1 Then
PrintObject.NewPage
End If
Else
PrintObject.Cls
End If
End Sub
After beginning a new page, I need to handle how I end the print session. This is even easier. I only need to execute the EndDoc method for the Printer object, since I don’t really want to clear the Picture control until after the user has finished viewing it. That will be handled automatically when I start the next print preview. The WriteEndDoc routine is shown in Listing 9.14.
Listing 9.14: WriteEndDoc Routine in Charter
Private Sub WriteEndDoc(PrintObject As Object)
If TypeOf PrintObject Is Printer Then
PrintObject.EndDoc
End If
End Sub
Listing 9.15 contains the simplest of the preview routines. It merely paints the specified image at the specified location. It really doesn’t need to be a separate routine; however, I included it here to show how the rest of the routines function. Basically, I supply all of the arguments necessary, followed by the PrinterObject, as parameters for this routine. Then I translate them into the single statement to paint a picture. Note that since both the Printer object and the Picture control both support the Line method, there isn’t any difference between how they are handled in this routine.
Listing 9.15: WritePicture Routine in Charter
Private Sub WritePicture(pic As Object, x1 As Single, y1 As Single, _
PrintObject As Object)
PrintObject.PaintPicture pic, x1, y1
End Sub
Remember how I described myself as lazy? The next routine, shown in Listing 9.16, is definitely one that looks nasty at first, but when you’ve had a chance to use it a bit, you will see how it can save a lot of work in the long run. This routine is called WriteCenter and has two siblings, WriteLeft and WriteRight, which you can read for yourself when you load this program.
Listing 9.16: WriteCenter Routine in Charter
Private Sub WriteCenter(OutputText As String, PrintObject As Object, _
Optional x1 As Variant, Optional y1 As Variant, _
Optional x2 As Variant, Optional y2 As Variant, _
Optional NewBold As Variant, Optional NewItalic As Variant, _
Optional NewUnderline As Variant, Optional NewSize As Variant, _
Optional NewFontName As Variant)
Dim OldBold As Boolean
Dim OldItalic As Boolean
Dim OldUnderline As Boolean
Dim OldSize As Integer
Dim OldFontName As String
If Not IsMissing(NewBold) Then
OldBold = PrintObject.Font.Bold
PrintObject.Font.Bold = NewBold
End If
If Not IsMissing(NewItalic) Then
OldItalic = PrintObject.Font.Italic
PrintObject.Font.Italic = NewItalic
End If
If Not IsMissing(NewUnderline) Then
OldUnderline = PrintObject.Font.Underline
PrintObject.Font.Underline = NewUnderline
End If
If Not IsMissing(NewSize) Then
OldSize = PrintObject.Font.Size
PrintObject.Font.Size = NewSize
End If
If Not IsMissing(NewFontName) Then
OldFontName = PrintObject.Font.Name
PrintObject.Font.Name = NewFontName
End If
If Not (IsMissing(x1) Or IsMissing(x2)) Then
PrintObject.CurrentX = x1 + (x2 - x1) / 2
ElseIf Not IsMissing(x1) Then
PrintObject.CurrentX = x1
End If
If Not (IsMissing(y1) Or IsMissing(y2)) Then
PrintObject.CurrentY = y1 + (y2 - y1) / 2
ElseIf Not IsMissing(y1) Then
PrintObject.CurrentY = y1
End If
PrintObject.CurrentX = PrintObject.CurrentX - _
PrintObject.TextWidth(OutputText) / 2
PrintObject.CurrentY = PrintObject.CurrentY - _
PrintObject.TextHeight(OutputText) / 2
PrintObject.Print OutputText
If Not IsMissing(NewFontName) Then
PrintObject.Font.Name = OldFontName
End If
If Not IsMissing(NewSize) Then
PrintObject.Font.Size = OldSize
End If
If Not IsMissing(NewUnderline) Then
PrintObject.Font.Underline = OldUnderline
End If
If Not IsMissing(NewItalic) Then
PrintObject.Font.Italic = OldItalic
End If
If Not IsMissing(NewBold) Then
PrintObject.Font.Bold = OldBold
End If
End Sub
All this routine does is to center the specified text string inside the rectangle defined the by x1, y1 and x2, y2 parameters, or centered at the location specified by x1, y1, or centered over the current cursor position. However, to make your life easier, I included most of the common font changes that you might make during the course of your program. Thus, you can easily specify any changes in the default font for a particular print item. This routine will ensure that only the specified changes are made and that the defaults are properly restored at the end of the routine.
This routine can be broken down into three parts. The first part checks to see if a parameter was supplied. If one is supplied, then the current property value is saved and the property is then set to the new value.
The second part is the actual positioning of the cursor and printing of the text. In this case, I compute the center point of where I want to print the text. If both x, y coordinates are supplied, I choose the center of the rectangle defined by them. If only one x, y coordinate is supplied, then I assume that the coordinate is the physical point where the text should be centered. If no x, y coordinates are supplied, then I assume that the current cursor position is where the text should be centered. Once I have the center point, all I do is subtract one half of the TextHeight method to determine the starting y coordinate, and subtract one half of the TextWidth method to determine the starting x coordinate. Then I can simply use the Print method to display the text.
The third part of the routine is to clean up any definitions I may have changed. Once that is done, I’m finished with this routine.
WARNING: Be careful of variable types when using Optional: The only acceptable variable type for an optional parameter is Variant. This means that it is easy to pass a String when you really need to pass an Integer. You should choose very meaningful variable names here to help the users determine the appropriate data type when Visual Basic prompts them with the list of parameters for the function.