home *** CD-ROM | disk | FTP | other *** search
- How to Add Print Preview to Visual Basic Applications [B_VBasic]
- ID: Q113236 CREATED: 29-MAR-1994 MODIFIED:
- 2.00 3.00
- Windows
- ENDUSER |
-
- --------------------------------------------------------------
- The information in this article applies to:
-
- - Standard and Professional Editions of Microsoft Visual Basic
- Programming System for Windows, version 2.0 and 3.0
- --------------------------------------------------------------
-
- SUMMARY
- =======
-
- This article describes how to create printing routines that can
- print to the printer or to a picture box. This enables you to add
- print preview capabilities to your Visual Basic applications.
-
- There are several ways that you could implement print preview in
- your applications. This article describes one method that is easy
- to do in Visual Basic and works well.
-
- MORE INFORMATION
- ================
-
- Generic Printing
- ----------------
-
- It would be ideal to have a generic print routine that could print
- to the printer or to the screen depending on what you pass it. The
- Visual Basic printer object and picture box control have many of the
- same methods and properties. For example, both of these are valid:
-
- Printer.Print AString
- Picture1.Print AString
-
- It would be nice if you could pass a generic object to a subroutine
- and the subroutine would use the Print method off of the generic object
- as in this example:
-
- Call PrintJob(Printer)
- Call PrintJob(Picture1)
-
- Sub PrintJob(GenericObject As Object)
- GenericObject.Print AString
- End Sub
-
- Unfortunately, this is not possible. The Visual Basic Printer object
- is a system object, so it can't be passed as a parameter.
-
- This leaves you with two choices in Visual Basic. You could create two
- routines -- one for printing to the printer and one for print preview.
- However, the code would not be reusable in your future projects. The
- second approach is to write your own set of routines that can print to
- the printer or a picture box based on the value of a flag. This is the
- method used in the example code given below. Once you create the
- routines, you can re-use them in future programs.
-
- The example creates routines that closely mimic Visual Basic's built in
- methods and properties. However, you could use this approach to create
- high-level routines that greatly simplify your printing needs.
-
- The routines work by checking the variable PrinterFlag. PrinterFlag is
- True when printing is going to the printer and False when printing to
- the picture box.
-
- Here's the print routine from the example. Notice how it is just a
- shell function that determines what to print to and then does it.
-
- Sub PrintPrint (PrintVar)
- If PrinterFlag Then
- Printer.Print PrintVar
- Else
- objPrint.Print PrintVar
- End If
- End Sub
-
- With just a few simple routines like this, you can start to do
- generic printing.
-
- Scaling
- -------
-
- To accomplish print preview, the program must scale the output to the
- picture box to match the output on the printer.
-
- In the example, the PrintStartDoc routine initializes the printer or
- picture box and sets up the scaling. The width and height of the paper
- are passed to the PrintStartDoc routine. These dimensions are used to
- determine the non-printable area of the printer object, find the ratio
- of the picture box to the printer, re-size the picture box, and scale
- the picture box. The picture box is scaled with the Scale method. After
- setting the scale of the picture box, graphic methods use the new
- coordinates. For an 8.5 x 11 inch piece of paper the picture box is
- scaled with this command:
-
- Picture1.Scale (0, 0)-(8.5, 11)
-
- The Scale method does not scale fonts. To scale the fonts, use the
- ratio of the picture box height divided by the printer's height in
- inches. Then multiply by this ratio to determine the correct font
- size within the picture box. Here is the PrintFontSize routine that
- sets the appropriate font sizes in the example:
-
- Sub PrintFontSize (pSize)
- If PrinterFlag Then
- Printer.FontSize = pSize
- Else
- 'Sized by ratio since Scale method does not effect FontSize
- ObjPrint.FontSize = pSize * Ratio
- End If
- End Sub
-
- The ratio used to calculate the font size can be applied to anything
- you need to scale in the picture box that is not automatically scaled
- by the Scale method. The ratio is also used in the PrintPicture routine
- to scale pictures.
-
- Step-by-Step Example
- --------------------
-
- 1. Start a new project in Visual Basic. Form1 is created by default.
-
- 2. Add a command button (Command1), a check box (Check1), and two
- picture boxes (Picture1 and Picture2) to the form.
-
- 3. Put the following code in the command button click event:
-
- Sub Command1_Click ()
- 'Setup (Could be done at design time or in form load)
- 'Make printing stick
- Picture1.AutoRedraw = True
- 'Add a palette for 256 colors
- Picture1.Picture = LoadPicture("C:\VB\PASTEL.DIB")
- 'Setup hidden picture
- Picture2.AutoRedraw = False
- Picture2.ScaleMode = 3 'Pixels
- Picture2.Visible = False
- Picture2.AutoSize = True
- Picture2.Picture = LoadPicture("C:\VB\METAFILE\BUSINESS\PRINTER.WMF")
-
- 'This print job can go to the printer or the picture box
- If Check1.Value = 0 Then PrinterFlag = True
- PrintStartDoc Picture1, PrinterFlag, 8.5, 11
-
- 'All the subs use inches
- PrintBox 1, 1, 6.5, 9
- PrintLine 1.1, 2, 7.4, 2
- PrintPicture Picture2, 1.1, 1.1, .8, .8
- PrintFilledBox 2.1, 1.2, 5.2, .7, RGB(200, 200, 200)
- PrintFontName "Arial"
- PrintCurrentX 2.3
- PrintCurrentY 1.3
- PrintFontSize 35
- PrintPrint "Visual Basic Printing"
- For x = 3 To 5.5 Step .2
- PrintCircle x, 3.5, .75
- Next
- PrintFontName "Courier New"
- PrintFontSize 30
- PrintCurrentX 1.5
- PrintCurrentY 5
- PrintPrint "It is possible to do"
- PrintFontSize 24
- PrintCurrentX 1.5
- PrintCurrentY 6.5
- PrintPrint "It is possible to do print"
- PrintFontSize 18
- PrintCurrentX 1.5
- PrintCurrentY 8
- PrintPrint "It is possible to do print preview"
- PrintFontSize 12
- PrintCurrentX 1.5
- PrintCurrentY 9.5
- PrintPrint "It is possible to do print preview with good results."
- PrintEndDoc
- End Sub
-
- 4. Add a new Module to the project (MODULE1.BAS).
-
- 5. Put the following code in the basic module:
-
- Option Explicit
-
- ' The following Types, Declares, and Constants are only necessary for the
-
- ' PrintPicture routine
- '=========================================================================
- =
- Type BITMAPINFOHEADER_TYPE
- biSize As Long
- biWidth As Long
- biHeight As Long
- biPlanes As Integer
- biBitCount As Integer
- biCompression As Long
- biSizeImage As Long
- biXPelsPerMeter As Long
- biYPelsPerMeter As Long
- biClrUsed As Long
- biClrImportant As Long
- bmiColors As String * 1024
- End Type
-
- Type BITMAPINFO_TYPE
- BitmapInfoHeader As BITMAPINFOHEADER_TYPE
- bmiColors As String * 1024
- End Type
-
- 'Each of the following declares should be entered on a single line
- Declare Function GetDIBits Lib "gdi" (ByVal hDC As Integer,
- ByVal hBitmap As Integer, ByVal nStartScan As Integer,
- ByVal nNumScans As Integer, ByVal lpBits As Long,
- BitmapInfo As BITMAPINFO_TYPE, ByVal wUsage As Integer) As Integer
- Declare Function StretchDIBits Lib "gdi" (ByVal hDC As Integer,
- ByVal DestX As Integer, ByVal DestY As Integer,
- ByVal wDestWidth As Integer, ByVal wDestHeight As Integer,
- ByVal SrcX As Integer, ByVal SrcY As Integer,
- ByVal wSrcWidth As Integer, ByVal wSrcHeight As Integer,
- ByVal lpBits As Long, BitsInfo As BITMAPINFO_TYPE,
- ByVal wUsage As Integer, ByVal dwRop As Long) As Integer
- Declare Function GlobalAlloc Lib "kernel" (ByVal wFlags As Integer,
- ByVal lMem As Long) As Integer
- Declare Function GlobalLock Lib "kernel" (ByVal hMem As Integer) As Long
- Declare Function GlobalUnlock Lib "kernel"
- (ByVal hMem As Integer) As Integer
- Declare Function GlobalFree Lib "kernel" (ByVal hMem As Integer) As Intege
- r
-
- Global Const SRCCOPY = &HCC0020
- Global Const BI_RGB = 0
- Global Const DIB_RGB_COLORS = 0
- Global Const GMEM_MOVEABLE = 2
-
- 'Module level variables set in PrintStartDoc
- 'Flag indicating Printing or Previewing
- Dim PrinterFlag
- 'Object used for Print Preview
- Dim ObjPrint As Control
- 'Storage for output objects original scale mode
- Dim sm
- 'The size ratio between the actual page and the print preview object
- Dim Ratio
- 'Size of the non-printable area on printer
- Dim LRGap
- Dim TBGap
- 'The actual paper size (8.5 x 11 normally)
- Dim PgWidth
- Dim PgHeight
-
- Sub PrintStartDoc (objToPrintOn As Control, PF, PaperWidth, PaperHeight)
- Dim psm
- Dim fsm
- Dim HeightRatio
- Dim WidthRatio
-
- 'Set the flag that determines whether printing or previewing
- PrinterFlag = PF
-
- 'Set the physical page size
- PgWidth = PaperWidth
- PgHeight = PaperHeight
-
- 'Find the size of the non-printable area on the printer
- 'Will be used to offset coordinates
- 'These formulas assume the non-printable area is centered on the page
- psm = Printer.ScaleMode
- Printer.ScaleMode = 5 'Inches
- LRGap = (PgWidth - Printer.ScaleWidth) / 2
- TBGap = (PgHeight - Printer.ScaleHeight) / 2
- Printer.ScaleMode = psm
-
- 'Initialize printer or preview object
- If PrinterFlag Then
- sm = Printer.ScaleMode
- Printer.ScaleMode = 5 'Inches
- Printer.Print "";
- Else
- 'Set the object used for preview
- Set ObjPrint = objToPrintOn
- 'Scale Object to Printer's printable area in Inches
- sm = ObjPrint.ScaleMode
- ObjPrint.ScaleMode = 5 'Inches
- 'Compare the height and with ratios to determine the
- 'Ratio to use and how to size the picture box
- HeightRatio = ObjPrint.ScaleHeight / PgHeight
- WidthRatio = ObjPrint.ScaleWidth / PgWidth
- If HeightRatio < WidthRatio Then
- Ratio = HeightRatio
- 'Re-size picture box - this does not work on a form
- fsm = ObjPrint.Parent.ScaleMode
- ObjPrint.Parent.ScaleMode = 5 'Inches
- ObjPrint.Width = PgWidth * Ratio
- ObjPrint.Parent.ScaleMode = fsm
- Else
- Ratio = WidthRatio
- 'Re-size picture box - this does not work on a form
- fsm = ObjPrint.Parent.ScaleMode
- ObjPrint.Parent.ScaleMode = 5 'Inches
- ObjPrint.Height = PgHeight * Ratio
- ObjPrint.Parent.ScaleMode = fsm
- End If
- 'Set default properties of picture box to match printer
- 'There are many that you could add here
- ObjPrint.Scale (0, 0)-(PgWidth, PgHeight)
- ObjPrint.FontName = Printer.FontName
- ObjPrint.FontSize = Printer.FontSize * Ratio
- ObjPrint.ForeColor = Printer.ForeColor
- ObjPrint.Cls
- End If
- End Sub
-
- Sub PrintCurrentX (XVal)
- If PrinterFlag Then
- Printer.CurrentX = XVal - LRGap
- Else
- ObjPrint.CurrentX = XVal
- End If
- End Sub
-
- Sub PrintCurrentY (YVal)
- If PrinterFlag Then
- Printer.CurrentY = YVal - TBGap
- Else
- ObjPrint.CurrentY = YVal
- End If
- End Sub
-
- Sub PrintFontName (pFontName)
- If PrinterFlag Then
- Printer.FontName = pFontName
- Else
- ObjPrint.FontName = pFontName
- End If
- End Sub
-
- Sub PrintFontSize (pSize)
- If PrinterFlag Then
- Printer.FontSize = pSize
- Else
- 'Sized by ratio since Scale method does not effect FontSize
- ObjPrint.FontSize = pSize * Ratio
- End If
- End Sub
-
- Sub PrintPrint (PrintVar)
- If PrinterFlag Then
- Printer.Print PrintVar
- Else
- ObjPrint.Print PrintVar
- End If
- End Sub
-
- Sub PrintLine (bLeft0, bTop0, bLeft1, bTop1)
- If PrinterFlag Then
- 'The following should be entered on a single line
- Printer.Line (bLeft0 - LRGap, bTop0 - TBGap)-
- (bLeft1 - LRGap, bTop1 - TBGap)
- Else
- ObjPrint.Line (bLeft0, bTop0)-(bLeft1, bTop1)
- End If
- End Sub
-
- Sub PrintBox (bLeft, bTop, bWidth, bHeight)
- If PrinterFlag Then
- 'The following should be entered on a single line
- Printer.Line (bLeft - LRGap, bTop - TBGap)-
- (bLeft + bWidth - LRGap, bTop + bHeight - TBGap), , B
- Else
- ObjPrint.Line (bLeft, bTop)-(bLeft + bWidth, bTop + bHeight), , B
- End If
- End Sub
-
- Sub PrintFilledBox (bLeft, bTop, bWidth, bHeight, color)
- If PrinterFlag Then
- 'The following should be entered on a single line
- Printer.Line (bLeft - LRGap, bTop - TBGap)-
- (bLeft + bWidth - LRGap, bTop + bHeight - TBGap), color, BF
- Else
- 'The following should be entered on a single line
- ObjPrint.Line (bLeft, bTop)-(bLeft + bWidth, bTop + bHeight),
- color, BF
- End If
- End Sub
-
- Sub PrintCircle (bLeft, bTop, bRadius)
- If PrinterFlag Then
- Printer.Circle (bLeft - LRGap, bTop - TBGap), bRadius
- Else
- ObjPrint.Circle (bLeft, bTop), bRadius
- End If
- End Sub
-
- Sub PrintNewPage ()
- If PrinterFlag Then
- Printer.NewPage
- Else
- ObjPrint.Cls
- End If
- End Sub
-
- 'The following should be entered on a single line
- Sub PrintPicture (picSource As Control, ByVal pLeft, ByVal pTop,
- ByVal pWidth, ByVal pHeight)
- 'Picture Box should have autoredraw = False, ScaleMode = Pixel
- ' Also can have visible=false, Autosize = true
-
- Dim BitmapInfo As BITMAPINFO_TYPE
- Dim DesthDC As Integer
- Dim hMem As Integer
- Dim lpBits As Long
- Dim r As Integer
-
- 'Precaution
- If pLeft < LRGap Or pTop < TBGap Then Exit Sub
- If pWidth < 0 Or pHeight < 0 Then Exit Sub
- If pWidth + pLeft > PgWidth - LRGap Then Exit Sub
- If pHeight + pTop > PgHeight - TBGap Then Exit Sub
- picSource.ScaleMode = 3 'Pixels
- picSource.AutoRedraw = False
- picSource.Visible = False
- picSource.AutoSize = True
-
- If PrinterFlag Then
- Printer.ScaleMode = 3 'Pixels
- 'Calculate size in pixels
- pLeft = ((pLeft - LRGap) * 1440) / Printer.TwipsPerPixelX
- pTop = ((pTop - TBGap) * 1440) / Printer.TwipsPerPixelY
- pWidth = (pWidth * 1440) / Printer.TwipsPerPixelX
- pHeight = (pHeight * 1440) / Printer.TwipsPerPixelY
- Printer.Print "";
- DesthDC = Printer.hDC
- Else
- ObjPrint.Scale
- ObjPrint.ScaleMode = 3 'Pixels
- 'Calculate size in pixels
- pLeft = ((pLeft * 1440) / Screen.TwipsPerPixelX) * Ratio
- pTop = ((pTop * 1440) / Screen.TwipsPerPixelY) * Ratio
- pWidth = ((pWidth * 1440) / Screen.TwipsPerPixelX) * Ratio
- pHeight = ((pHeight * 1440) / Screen.TwipsPerPixelY) * Ratio
- DesthDC = ObjPrint.hDC
- End If
-
- BitmapInfo.BitmapInfoHeader.biSize = 40
- BitmapInfo.BitmapInfoHeader.biWidth = picSource.ScaleWidth
- BitmapInfo.BitmapInfoHeader.biHeight = picSource.ScaleHeight
- BitmapInfo.BitmapInfoHeader.biPlanes = 1
- BitmapInfo.BitmapInfoHeader.biBitCount = 8
- BitmapInfo.BitmapInfoHeader.biCompression = BI_RGB
-
- 'Enter the following on a single line
- hMem = GlobalAlloc(GMEM_MOVEABLE, (CLng(picSource.ScaleWidth + 3) \ 4)
- *
- 4 * picSource.ScaleHeight)'DWORD ALIGNED
- lpBits = GlobalLock(hMem)
-
- 'Enter the following on a single line
- r = GetDIBits(picSource.hDC, picSource.Image, 0, picSource.ScaleHeight,
-
- lpBits, BitmapInfo, DIB_RGB_COLORS)
- If r <> 0 Then
- 'Enter the following on a single line
- r = StretchDIBits(DesthDC, pLeft, pTop, pWidth, pHeight, 0, 0,
- picSource.ScaleWidth, picSource.ScaleHeight, lpBits, BitmapInfo,
-
- DIB_RGB_COLORS, SRCCOPY)
- End If
-
- r = GlobalUnlock(hMem)
- r = GlobalFree(hMem)
-
- If PrinterFlag Then
- Printer.ScaleMode = 5 'Inches
- Else
- ObjPrint.ScaleMode = 5'Inches
- ObjPrint.Scale (0, 0)-(PgWidth, PgHeight)
- End If
- End Sub
-
- Sub PrintEndDoc ()
- If PrinterFlag Then
- Printer.EndDoc
- Printer.ScaleMode = sm
- Else
- ObjPrint.ScaleMode = sm
- End If
- End Sub
-
- 6. Save the project.
-
- 7. Run it.
-
- Click the command button with the check box checked to preview the
- page. Click the command button with the check box cleared to print
- the page.
-
- Notes
- -----
-
- - The accuracy of the preview really depends on the fonts available.
- This method relies upon Windows to return the most appropriate font
- and size. You could come up with your own algorythm for choosing a
- font size. The TextWidth and TextHeight methods of the Printer
- object and picture box may be useful for this.
-
- - The example uses inches the device independent unit of measurement.
- But you could use twips, points, millimeters, or centemeters.
-
- - You may want to implement the preview window in a scrollable viewport.
- For more information, please see the following article in the Microsoft
- Knowledge Base:
-
- ARTICLE-ID: Q71068
- TITLE : How to Create Scrollable Viewports in Visual Basic
-
- - There are other methods that you could use to preview printing. You
- could pass a device context to a routine that uses only Windows API
- functions to draw and print. You could also create a picture that you
- either stretch to the printer or to the screen.
-
- Additional reference words: 2.00 3.00
- KBCategory: APrg
- KBSubcategory: APrgPrint
- \*
- \* MSINTERNAL:
- \* Tested with: HP Laserjet II
- \* HP LaserJet III
- \* HP LaserJet IIIsi
- \* HP LaserJet IIIsi PostScript
- \* Compaq QVision 1024*768*256 Lg. Res
- \* ATI Graphics Ultra 800*600*256
- \* VGA
- \* Tech Review: LyleH
- ================================================================================
- Created_by: TIMMCB Edit_review: Edited:
- Modified_by: Tech_review: Reviewed:
-