- •Using Your Sybex Electronic Book
- •Acknowledgments
- •Contents at a Glance
- •Introduction
- •Who Should Read This Book?
- •How About the Advanced Topics?
- •The Structure of the Book
- •How to Reach the Author
- •The Integrated Development Environment
- •The Start Page
- •Project Types
- •Your First VB Application
- •Making the Application More Robust
- •Making the Application More User-Friendly
- •The IDE Components
- •The IDE Menu
- •The Toolbox Window
- •The Solution Explorer
- •The Properties Window
- •The Output Window
- •The Command Window
- •The Task List Window
- •Environment Options
- •A Few Common Properties
- •A Few Common Events
- •A Few Common Methods
- •Building a Console Application
- •Summary
- •Building a Loan Calculator
- •How the Loan Application Works
- •Designing the User Interface
- •Programming the Loan Application
- •Validating the Data
- •Building a Math Calculator
- •Designing the User Interface
- •Programming the MathCalculator App
- •Adding More Features
- •Exception Handling
- •Taking the LoanCalculator to the Web
- •Working with Multiple Forms
- •Working with Multiple Projects
- •Executable Files
- •Distributing an Application
- •VB.NET at Work: Creating a Windows Installer
- •Finishing the Windows Installer
- •Running the Windows Installer
- •Verifying the Installation
- •Summary
- •Variables
- •Declaring Variables
- •Types of Variables
- •Converting Variable Types
- •User-Defined Data Types
- •Examining Variable Types
- •Why Declare Variables?
- •A Variable’s Scope
- •The Lifetime of a Variable
- •Constants
- •Arrays
- •Declaring Arrays
- •Initializing Arrays
- •Array Limits
- •Multidimensional Arrays
- •Dynamic Arrays
- •Arrays of Arrays
- •Variables as Objects
- •So, What’s an Object?
- •Formatting Numbers
- •Formatting Dates
- •Flow-Control Statements
- •Test Structures
- •Loop Structures
- •Nested Control Structures
- •The Exit Statement
- •Summary
- •Modular Coding
- •Subroutines
- •Functions
- •Arguments
- •Argument-Passing Mechanisms
- •Event-Handler Arguments
- •Passing an Unknown Number of Arguments
- •Named Arguments
- •More Types of Function Return Values
- •Overloading Functions
- •Summary
- •The Appearance of Forms
- •Properties of the Form Control
- •Placing Controls on Forms
- •Setting the TabOrder
- •VB.NET at Work: The Contacts Project
- •Anchoring and Docking
- •Loading and Showing Forms
- •The Startup Form
- •Controlling One Form from within Another
- •Forms vs. Dialog Boxes
- •VB.NET at Work: The MultipleForms Project
- •Designing Menus
- •The Menu Editor
- •Manipulating Menus at Runtime
- •Building Dynamic Forms at Runtime
- •The Form.Controls Collection
- •VB.NET at Work: The DynamicForm Project
- •Creating Event Handlers at Runtime
- •Summary
- •The TextBox Control
- •Basic Properties
- •Text-Manipulation Properties
- •Text-Selection Properties
- •Text-Selection Methods
- •Undoing Edits
- •VB.NET at Work: The TextPad Project
- •Capturing Keystrokes
- •The ListBox, CheckedListBox, and ComboBox Controls
- •Basic Properties
- •The Items Collection
- •VB.NET at Work: The ListDemo Project
- •Searching
- •The ComboBox Control
- •The ScrollBar and TrackBar Controls
- •The ScrollBar Control
- •The TrackBar Control
- •Summary
- •The Common Dialog Controls
- •Using the Common Dialog Controls
- •The Color Dialog Box
- •The Font Dialog Box
- •The Open and Save As Dialog Boxes
- •The Print Dialog Box
- •The RichTextBox Control
- •The RTF Language
- •Methods
- •Advanced Editing Features
- •Cutting and Pasting
- •Searching in a RichTextBox Control
- •Formatting URLs
- •VB.NET at Work: The RTFPad Project
- •Summary
- •What Is a Class?
- •Building the Minimal Class
- •Adding Code to the Minimal Class
- •Property Procedures
- •Customizing Default Members
- •Custom Enumerations
- •Using the SimpleClass in Other Projects
- •Firing Events
- •Shared Properties
- •Parsing a Filename String
- •Reusing the StringTools Class
- •Encapsulation and Abstraction
- •Inheritance
- •Inheriting Existing Classes
- •Polymorphism
- •The Shape Class
- •Object Constructors and Destructors
- •Instance and Shared Methods
- •Who Can Inherit What?
- •Parent Class Keywords
- •Derived Class Keyword
- •Parent Class Member Keywords
- •Derived Class Member Keyword
- •MyBase and MyClass
- •Summary
- •On Designing Windows Controls
- •Enhancing Existing Controls
- •Building the FocusedTextBox Control
- •Building Compound Controls
- •VB.NET at Work: The ColorEdit Control
- •VB.NET at Work: The Label3D Control
- •Raising Events
- •Using the Custom Control in Other Projects
- •VB.NET at Work: The Alarm Control
- •Designing Irregularly Shaped Controls
- •Designing Owner-Drawn Menus
- •Designing Owner-Drawn ListBox Controls
- •Using ActiveX Controls
- •Summary
- •Programming Word
- •Objects That Represent Text
- •The Documents Collection and the Document Object
- •Spell-Checking Documents
- •Programming Excel
- •The Worksheets Collection and the Worksheet Object
- •The Range Object
- •Using Excel as a Math Parser
- •Programming Outlook
- •Retrieving Information
- •Recursive Scanning of the Contacts Folder
- •Summary
- •Advanced Array Topics
- •Sorting Arrays
- •Searching Arrays
- •Other Array Operations
- •Array Limitations
- •The ArrayList Collection
- •Creating an ArrayList
- •Adding and Removing Items
- •The HashTable Collection
- •VB.NET at Work: The WordFrequencies Project
- •The SortedList Class
- •The IEnumerator and IComparer Interfaces
- •Enumerating Collections
- •Custom Sorting
- •Custom Sorting of a SortedList
- •The Serialization Class
- •Serializing Individual Objects
- •Serializing a Collection
- •Deserializing Objects
- •Summary
- •Handling Strings and Characters
- •The Char Class
- •The String Class
- •The StringBuilder Class
- •VB.NET at Work: The StringReversal Project
- •VB.NET at Work: The CountWords Project
- •Handling Dates
- •The DateTime Class
- •The TimeSpan Class
- •VB.NET at Work: Timing Operations
- •Summary
- •Accessing Folders and Files
- •The Directory Class
- •The File Class
- •The DirectoryInfo Class
- •The FileInfo Class
- •The Path Class
- •VB.NET at Work: The CustomExplorer Project
- •Accessing Files
- •The FileStream Object
- •The StreamWriter Object
- •The StreamReader Object
- •Sending Data to a File
- •The BinaryWriter Object
- •The BinaryReader Object
- •VB.NET at Work: The RecordSave Project
- •The FileSystemWatcher Component
- •Properties
- •Events
- •VB.NET at Work: The FileSystemWatcher Project
- •Summary
- •Displaying Images
- •The Image Object
- •Exchanging Images through the Clipboard
- •Drawing with GDI+
- •The Basic Drawing Objects
- •Drawing Shapes
- •Drawing Methods
- •Gradients
- •Coordinate Transformations
- •Specifying Transformations
- •VB.NET at Work: Plotting Functions
- •Bitmaps
- •Specifying Colors
- •Defining Colors
- •Processing Bitmaps
- •Summary
- •The Printing Objects
- •PrintDocument
- •PrintDialog
- •PageSetupDialog
- •PrintPreviewDialog
- •PrintPreviewControl
- •Printer and Page Properties
- •Page Geometry
- •Printing Examples
- •Printing Tabular Data
- •Printing Plain Text
- •Printing Bitmaps
- •Using the PrintPreviewControl
- •Summary
- •Examining the Advanced Controls
- •How Tree Structures Work
- •The ImageList Control
- •The TreeView Control
- •Adding New Items at Design Time
- •Adding New Items at Runtime
- •Assigning Images to Nodes
- •Scanning the TreeView Control
- •The ListView Control
- •The Columns Collection
- •The ListItem Object
- •The Items Collection
- •The SubItems Collection
- •Summary
- •Types of Errors
- •Design-Time Errors
- •Runtime Errors
- •Logic Errors
- •Exceptions and Structured Exception Handling
- •Studying an Exception
- •Getting a Handle on this Exception
- •Finally (!)
- •Customizing Exception Handling
- •Throwing Your Own Exceptions
- •Debugging
- •Breakpoints
- •Stepping Through
- •The Local and Watch Windows
- •Summary
- •Basic Concepts
- •Recursion in Real Life
- •A Simple Example
- •Recursion by Mistake
- •Scanning Folders Recursively
- •Describing a Recursive Procedure
- •Translating the Description to Code
- •The Stack Mechanism
- •Stack Defined
- •Recursive Programming and the Stack
- •Passing Arguments through the Stack
- •Special Issues in Recursive Programming
- •Knowing When to Use Recursive Programming
- •Summary
- •MDI Applications: The Basics
- •Building an MDI Application
- •Built-In Capabilities of MDI Applications
- •Accessing Child Forms
- •Ending an MDI Application
- •A Scrollable PictureBox
- •Summary
- •What Is a Database?
- •Relational Databases
- •Exploring the Northwind Database
- •Exploring the Pubs Database
- •Understanding Relations
- •The Server Explorer
- •Working with Tables
- •Relationships, Indices, and Constraints
- •Structured Query Language
- •Executing SQL Statements
- •Selection Queries
- •Calculated Fields
- •SQL Joins
- •Action Queries
- •The Query Builder
- •The Query Builder Interface
- •SQL at Work: Calculating Sums
- •SQL at Work: Counting Rows
- •Limiting the Selection
- •Parameterized Queries
- •Calculated Columns
- •Specifying Left, Right, and Inner Joins
- •Stored Procedures
- •Summary
- •How About XML?
- •Creating a DataSet
- •The DataGrid Control
- •Data Binding
- •VB.NET at Work: The ViewEditCustomers Project
- •Binding Complex Controls
- •Programming the DataAdapter Object
- •The Command Objects
- •The Command and DataReader Objects
- •VB.NET at Work: The DataReader Project
- •VB.NET at Work: The StoredProcedure Project
- •Summary
- •The Structure of a DataSet
- •Navigating the Tables of a DataSet
- •Updating DataSets
- •The DataForm Wizard
- •Handling Identity Fields
- •Transactions
- •Performing Update Operations
- •Updating Tables Manually
- •Building and Using Custom DataSets
- •Summary
- •An HTML Primer
- •HTML Code Elements
- •Server-Client Interaction
- •The Structure of HTML Documents
- •URLs and Hyperlinks
- •The Basic HTML Tags
- •Inserting Graphics
- •Tables
- •Forms and Controls
- •Processing Requests on the Server
- •Building a Web Application
- •Interacting with a Web Application
- •Maintaining State
- •The Web Controls
- •The ASP.NET Objects
- •The Page Object
- •The Response Object
- •The Request Object
- •The Server Object
- •Using Cookies
- •Handling Multiple Forms in Web Applications
- •Summary
- •The Data-Bound Web Controls
- •Simple Data Binding
- •Binding to DataSets
- •Is It a Grid, or a Table?
- •Getting Orders on the Web
- •The Forms of the ProductSearch Application
- •Paging Large DataSets
- •Customizing the Appearance of the DataGrid Control
- •Programming the Select Button
- •Summary
- •How to Serve the Web
- •Building a Web Service
- •Consuming the Web Service
- •Maintaining State in Web Services
- •A Data-Driven Web Service
- •Consuming the Products Web Service in VB
- •Summary
706 Chapter 15 PRINTING WITH VB.NET
The first example of this chapter, discussed a few pages earlier in Listing 15.1, prints three simple pages to the printer. To redirect the output of the program to the PrintPreview control, replace the statement that calls the PrintDocument1.Print method in the button’s Click event handler with the following statements:
PrintPreviewDialog1.Document = PrintDocument1
PrintPreviewDialog1.ShowDialog
If you run the project, this time you’ll be able to preview the document on your monitor. If you’re satisfied with its appearance, you can click the Print button to send the document to the printer. As you can see, providing a Print Preview feature is really trivial. All the work is done by the PrintPreviewDialog control.
To avoid runtime errors, you can use the following exception handler:
Try
PrintPreviewDialog1.Document = PrintDocument1
PrintPreviewDialog1.ShowDialog
Catch exc As Exception
MsgBox “The printing operation failed” & vbCrLf & exc.Message
End Try
PrintPreviewControl
The PrintPreviewControl control is the preview pane of the Print Preview dialog box. The control has no buttons, and you must provide your own interface to allow users to navigate through the document’s pages or change the current magnification. There are no compelling reasons to use this control, but it’s an alternative to the PrintPreviewDialog control. Once you’ve understood how the PrintPreviewDialog control works, you will find it easy to program to control and preview the documents on a form of your own.
At this point, you’d expect to see some more examples of printing. However, there are a few more objects we’ll be using in our examples, and I must discuss them before I can show you some nontrivial examples. Please bear with me for a few more pages and you’ll find several examples of common printing tasks shortly. In the next two sections, you’ll learn how to retrieve the dimensions of the page in the printer and how to specify coordinates on the page. This will allow you to generate printouts that are not tied to a specific page size and/or orientation.
Printer and Page Properties
One of the most common tasks in generating printouts is to retrieve the settings of the current printer and page, and this is a good place to present the members of these two objects, as we’ll use them extensively in the examples of the following sections. The properties of these two items are reported to your application through the PrinterSettings and the PageSettings objects. The PageSettings object is a property of the PrintPageEventArgs class, and you can access it through the e argument of the PrintPage event handler. The DefaultPageSettings property of the PrintDocument object is an object, which exposes the current page’s settings.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
PRINTER AND PAGE PROPERTIES 707
The PrinterSettings object is a property of the PrintDocument object, as well as a property of the PageSetupDialog and PrintDialog controls. Finally, one of the properties exposed by the PageSettings object is the PrinterSettings object. These two objects provide all the information you may need about the selected printer and the current page through the properties listed in Tables 15.1 and 15.2.
Table 15.1: The Properties of the PageSettings Object
Property |
Description |
Bounds |
Returns the bounds of the page (Bounds.Width and Bounds.Height). If the current |
|
orientation is landscape, the width is larger than the height. |
Color |
Returns, or sets, a True/False value indicating whether the current page should be |
|
printed in color. On a monochrome printer, this property is always False. |
Landscape |
A True/False value indicating whether the page is printed in landscape or portrait |
|
orientation. |
Margins |
The margins for the current page (Margins.Left, Margins.Right, |
|
Margins.Bottom, and Margins.Top). |
PaperSize |
The size of the current page (PaperSize.Width and PaperSize.Height). |
PaperSource |
The page’s paper tray. |
PrinterResolution |
The printer’s resolution for the current page. |
PrinterSettings |
This property returns, or sets, the printer settings associated with the page. For more |
|
information on the PrinterSettings object and the properties it exposes, see the follow- |
|
ing table. |
|
|
Table 15.2: The Members of the PrinterSettings Object
Member |
Description |
InstalledPrinters |
This method retrieves the names of all printers installed on the computer. The |
|
same printer names also appear in the Print dialog box, where the user can select |
|
any one of them. |
CanDuplex |
A read-only property that returns a True/False value indicating whether the |
|
printer supports double-sided printing. |
Collate |
Another read-only property that returns a True/False value indicating whether |
|
the printout should be collated or not. |
Copies |
This property returns the requested number of copies of the printout. |
DefaultPageSettings |
This property is the PageSettings object that returns, or sets, the default page set- |
|
tings for the current printer. |
Duplex |
This property returns, or sets, the current setting for double-sided printing. |
|
Continued on next page |
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
708 Chapter 15 PRINTING WITH VB.NET
Table 15.2: The Members of the PrinterSettings Object (continued)
Member |
Description |
FromPage, ToPage |
The printout’s starting and ending pages, as specified in the Print dialog box by |
|
the user. |
IsDefaultPrinter |
Returns a True/False value indicating whether the selected printer (the one iden- |
|
tified by the PrinterName property) is the default printer. Note that selecting a |
|
printer other than the default one in the Print dialog box doesn’t change the |
|
default printer. |
IsPlotter |
Returns a True/False value indicating whether the printer is a plotter. |
IsValid |
Returns a True/False value indicating whether the PrinterName corresponds to a |
|
valid printer. |
LandscapeAngle |
Returns an angle, in degrees, by which the portrait orientation must be rotated to |
|
produce the landscape orientation. |
MaximumCopies |
Returns the maximum number of copies that the printer allows you to print at |
|
a time. |
MaximumPage |
Returns, or sets, the largest value the FromPage and ToPage properties can have. |
MinimumPage |
Returns, or sets, the smallest value the FromPage and ToPage properties can have. |
PaperSizes |
Returns all the paper sizes that are supported by this printer. |
PaperSources |
Returns all the paper source trays on the selected printer. |
PrinterName |
Returns, or sets, the name of the printer to use. |
PrinterResolutions |
Returns all the resolutions that are supported by this printer. |
PrintRange |
Returns, or sets, the numbers of the pages to be printed, as specified by the user. |
|
When you set this property, the value becomes the default setting when the Print |
|
dialog box is opened. |
SupportsColor |
Returns a True/False value indicating whether this printer supports color printing. |
CreateMeasurementGraphics |
Returns a Graphics object that contains printer information you can use in the |
|
PrintDocument.Print event handler. |
|
|
To retrieve the names of the installed printers, use the InstalledPrinters collection of the PrinterSettings object. This collection contains the names of the printers as strings, and you can access them with the following loop:
Dim i As Integer
With PrintDocument1.PrinterSettings.InstalledPrinters
For i = 0 To .Count – 1
Console.WriteLine(.Item(i))
Next
End With
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
PRINTER AND PAGE PROPERTIES 709
These statements will produce output like the following when executed:
Fax HPLaser
\\EXPERT\XEROX
The first two printers are local (Fax isn’t even a printer; it’s a driver for the fax and it’s installed by Windows). The last printer’s name is XEROX and it’s a network printer, connected to the EXPERT workstation.
You can also change the current printer by setting the PrinterName property of the PrinterSettings property with either of the following statements:
PrintDocument1.PrinterSettings.PrinterName = “HPLaser”
PrintDocument1.PrinterSettings.PrinterName = _
PrintDocument1.PrinterSettings.InstalledPrinters(1)
Another property that needs additional explanation is the PrinterResolution object. The PrinterResolution object provides the Kind property, which returns, or sets, the current resolution of the printer, and its value is one of the PrinterResolutionKind enumeration’s members: Custom, Draft, High, Low, and Medium. To find out the exact horizontal and vertical resolutions, read the X and Y properties of the PrinterResolution object. When you set the PrinterResolution.Kind property to Custom, you must specify the X and Y properties.
Note that the PrinterResolution object is a property of the PageSettings object. The PrinterSettings object exposes a similarly named object, the PrinterResolutions property. This property is a collection that returns all resolution kinds, which are the members of the PrinterResolutionKind enumeration.
Page Geometry
Printing on a page is similar to generating graphics on your screen. Like the drawing surface on the monitor (the client area), the page on which you’re printing has a fixed size and resolution. The most challenging aspect of printing is the calculation of the coordinates, where each graphic element will appear. In business applications, the most common elements are strings (rendered in various fonts, styles, and sizes), lines, and rectangles, which are used as borders for tabular data. Although you can print anywhere on the page (if you want, you can start filling the page from the bottom up), we usually print one element at a time, calculate the space it takes on the page, and then print the next element next or below it. Printing code makes heavy use of the MeasureString method, and nearly all of the examples in this chapter make use of this method.
The printable area is determined by the size of the paper you’re using, and in most cases it’s 8.5×11 inches (keep in mind that most printers can’t print near the edge of the page). Printed pages have a margin on all four sides, and you can set a different margin on every side. The user determines the margins through the Page Setup dialog box, and your program is confined to drawing within the specified margins.
You can access the current page’s margins through the Margins property of the PrintDocument1
.DefaultPageSettings object. This object exposes the Left, Right, Top, and Bottom properties, which are the values of the four margins. The margins, as well as the page coordinates, are expressed in hundredths of an inch. The width of a standard letter-sized page, for example, is 8,500 units and its height
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
710 Chapter 15 PRINTING WITH VB.NET
is 11,000 units. Of course, you can use non-integer values for even greater granularity, but you won’t see two straight lines printed at less than one hundredth of an inch apart. You can use other units, which are all members of the PageUnit enumeration, which was discussed in the previous chapter.
Note In the examples of this chapter, I’m using the default units, and since there are 100 units in an inch, all the variables that represent coordinates and sizes are declared as Integer. You can also declare them as Single, but a fraction of a hundredth of an inch isn’t going to make any difference in your printout.
Another property exposed by the DefaultSettings object is the PageSize property, which in turn exposes the Width and Height properties. The width and height of the page are given by the following expressions:
PrintDocument1.DefaultPageSettings.PaperSize.Width
PrintDocument1.DefaultPageSettings.PaperSize.Height
The top of the page is at coordinates (0, 0), which corresponds to the top-left corner of the page. We never actually print at this corner. The coordinates of the top-left corner of the printable area of the page are given by the following expressions:
PrintDocument1.DefaultPageSettings.Margins.Top
PrintDocument1.DefaultPageSettings.Margins.Left
VB.NET at Work: The SimplePrintout Project
Let’s start with a very simple application that prints a string at the top-left corner of the page (the origin of the page) and a rectangle that delimits the page’s printable area. To print something, start by dropping the PrintDocument object on your form. An instance of the control will appear in the Components tray, as this control is invisible at runtime. Then place a button on the form and in its Click event handler enter the following statement:
PrintDocument1.Print()
This tells the PrintDocument object that you’re ready to print. The PrintDocument object will fire the BeginPrint event, where you can place any initialization code. Then, it will fire the PrintPage event, whose definition is
Private Sub PrintDocument1_PrintPage(ByVal sender As Object, _
ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
Handles PrintDocument1.PrintPage
End Sub
This event doesn’t signal an external action; it’s the PrintDocument object’s way of receiving your printing statements (it tells the application “I’m ready to print another page, so please tell me what to print”). As implied by its name, the PrintPage event is fired once for each page. You must place the VB code required to produce the desired output in this event’s handler. So, how do we access the printer from within this event?
The e argument exposes several members, the most important of them being the Graphics property. The e.Graphics property is a Graphics object, which you can use to draw on the page. Anything you draw on this object is printed on paper. As you recall from the previous chapter, the
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
PRINTER AND PAGE PROPERTIES 711
e.Graphics object represents the drawing surface. When printing, the same object represents the paper in your printer’s tray.
Let’s start by printing a single word at the origin of the page, which is its top-left corner. To draw a string, call the Graphic object’s DrawString method, as shown here:
Dim pFont As Font
pFont = New Font(“Comic Sans MS”, 20) e.Graphics.DrawString(“ORIGIN”, pFont, Brushes.Black, 0, 0)
The last two arguments of the DrawString method are the coordinates of a point, where the string will be printed. As you notice, the string is printed below the origin so that it’s visible. If you attempt to print a string at the bottom-right corner of the page, the entire string will fall just outside the page and no visible output will be produced.
No matter what your default printer is, it’s highly unlikely that it’s been set to no margins. As you can see, the page’s margins aren’t enforced by the PrintDocument object; you must respect them from within your code, as it is possible to print anywhere on the page. To take into consideration the page’s margins, change the coordinates from (0, 0) to the left and top margins.
You can also use the other members of the Graphics object, as you did in the previous chapter to generate graphics. The following statement will render the text on the page using an anti-alias technique (anti-aliased text looks much smoother than text rendered with the default method):
e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
Next, we’ll print a rectangle around the area of page in which we’re allowed to print—a rectangle delimited by the margins of the page. To draw this rectangle, we need to know the size of all four margins, and the size of the page, obviously. To read (or set) the page’s margins, use the PrintDocument1.DefaultPageSettings.Margin object, which provides the Left, Right, Top, and Bottom properties. We’re also going to need the dimensions of the page, which we can read through the Width
and Height properties of the PrintDocument1.DefaultPageSettings.PaperSize object. The four
margins are calculated and stored in four variables with the following statements:
Dim Lmargin, Rmargin, Tmargin, Bmargin As Integer
With PrintDocument1.DefaultPageSettings.Margins
Lmargin = .Left
Rmargin = .Right
Tmargin = .Top
Bmargin = .Bottom
End With
The rectangle we want to draw should start at the point (Lmargin, Tmargin) and extend PrintWidth units to the right and PrintHeight units down. These two variables are the width and height of the page minus the respective margins, and they’re calculated with the following statements:
Dim PrintWidth, PrintHeight As Integer
With PrintDocument1.DefaultPageSettings.PaperSize
PrintWidth = .Width - Lmargin - Rmargin
PrintHeight = .Height - Tmargin - Bmargin
End With
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
712 Chapter 15 PRINTING WITH VB.NET
Then insert the following statements in the PrintPage event handler to draw the rectangle:
Dim R As Rectangle
R = New Rectangle(Lmargin, Tmargin, PrintWidth, PrintHeight) e.Graphics.DrawRectangle(Pens.Black, R)
You’ve seen all the statements that will generate the desired output and they’re all familiar to you from the previous chapter. You will find these statements in the SimplePrintout project, which consists of a form with a button. In summary, here’s how you’ll build the SimplePrintout project from scratch. First, create the interface (a single button on the form will suffice). Then drop an instance of the PrintDocument control on the form. This control must exist on every form that sends data to the printer. The code behind the button initiates the printing with the following statement:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
PrintDocument1.Print()
End Sub
The action takes place from within the PrintPage event handler, which is shown in Listing 15.2. The event handler contains all the statements presented in the previous paragraphs and a few comments.
Listing 15.2: Generating a Simple Printout
Private Sub PrintDocument1_PrintPage(ByVal sender As Object,_
ByVal e As System.Drawing.Printing.PrintPageEventArgs) _ Handles PrintDocument1.PrintPage
‘ Turn on antialias for text
e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
‘Print a string at the origin Dim pFont As Font
pFont = New Font(“Comic Sans MS”, 20) e.Graphics.DrawString(“ORIGIN”, pFont, Brushes.Black, 0, 0)
‘Read margins into local variables
Dim Lmargin, Rmargin, Tmargin, Bmargin As Integer
With PrintDocument1.DefaultPageSettings.Margins
Lmargin = .Left
Rmargin = .Right
Tmargin = .Top
Bmargin = .Bottom
End With
‘Calculate the dimensions of the printable area Dim PrintWidth, PrintHeight As Integer
With PrintDocument1.DefaultPageSettings.PaperSize PrintWidth = .Width - Lmargin - Rmargin PrintHeight = .Height - Tmargin - Bmargin
End With
‘Now print the rectangle
Dim R As Rectangle
R = New Rectangle(Lmargin, Tmargin, PrintWidth, PrintHeight) e.Graphics.DrawRectangle(Pens.Black, R)
End Sub
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
PRINTER AND PAGE PROPERTIES 713
VB.NET at Work: The PageSettings Project
Let’s put together the information presented so far by printing something more elaborate. This example prints a rectangle bounded by the margins of the page as before. In addition to the rectangle, it also prints four strings, one in each margin (as seen in Figure 15.5). These strings will have different orientations, as shown in the following figure. The project that generated the output is called PageSettings, and you will find it in this chapter’s folder on the CD. The same project also demonstrates how to display the PageSetup dialog box from within your code and generate a printout according to the settings on this dialog box. It may not be the most practical example, but it demonstrates some basic printing techniques.
Figure 15.5
The output of the
PageSettings project
The statements of the previous example print a rectangle enclosing the printable area of the page. Printing the labels is a bit involved. As you can see, the four strings appear in all four orientations, and therefore some rotation transformation is involved. We’ll discuss the code for printing the captions later. For now, let’s demonstrate the PageSetupDialog control and how you take the settings on this dialog box from within your code.
Setting Up the Page
To display this dialog box from within your code, first place an instance of the PageSetupDialog control on your form—it will actually appear in the Components tray below the form, because it’s invisible at runtime. Then set its PageSettings property to a PageSettings object that contains the default settings for the printer. We usually set this property to the DefaultPageSettings property of the PrintDocument object, although you can create a new PageSettings object and set its properties from within your code. Finally, display the dialog box by calling its ShowDialog method:
PageSetupDialog1.PageSettings = PrintDocument1.DefaultPageSettings
PageSetupDialog1.ShowDialog()
PrintDocument1.DefaultPageSettings = PageSetupDialog1.PageSettings
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
714 Chapter 15 PRINTING WITH VB.NET
Upon return, we assign the PageSettings property of the control to the DefaultPageSettings property of the PrintDocument object. Now, we must take into consideration the settings specified on the dialog box from within the PrintPage event’s code. The area on the page in which you must restrict your output is a rectangle, with its top-left corner at the left and top margin, and its dimensions are the width and height of the page less the corresponding margins. The only implication is when the user changes the orientation of the page. When you’re printing in landscape mode, the size of the paper doesn’t change. If you examine the Width and Height properties of the PaperSize object, you’ll realize that the page is always taller than it is wide. This means that we must swap the width and height from within our code. The margins, however, remain the same.
To find out whether the user has changed the page’s orientation, examine the Landscape property of the DefaultPageSettings object. If it’s True, it means that the user wants to print in landscape mode and you must swap the page’s width and height units. The following statements calculate the dimensions of the area of the page within the margins, where all the printing will take place, as well as the new width and height of the page:
If PrintDocument1.DefaultPageSettings.Landscape Then
With PrintDocument1.DefaultPageSettings.PaperSize
PrintWidth = .Height - Tmargin - Bmargin
PrintHeight = .Width - Rmargin - Lmargin
PageWidth = .Height
PageHeight = .Width
End With
End If
If you run the PageSettings project now, you will see that it prints the string “ORIGIN” at the top-left corner of the page, regardless of the page’s orientation. These few statements in effect turn around the “image” of the printout, and the rest of the code will work in either orientation.
Printing the Labels
Now we can focus on the code that prints the captions in the space of the four margins, which is considerably more elaborate. The top margin’s caption isn’t rotated; it’s printed at the default orientation. The caption in the right margin is rotated by 90 degrees, and the caption of the bottom margin is rotated by 180 degrees. The caption of the left margin is rotated by –90 degrees. These rotations take place around the origin, so the labels must also be moved to their places with a translation transformation.
Let’s look at the code that prints the “Right Margin String” caption, shown in Listing 15.3. The following statements are responsible for printing the caption in the right margin. They make use of the variables that hold the margins and the page’s dimensions, which were discussed already.
Listing 15.3: Printing a Caption in the Right Margin
strWidth = e.Graphics.MeasureString(RMarginCaption, pFont).Width strHeight = e.Graphics.MeasureString(RMarginCaption, pFont).Height X = PageWidth - (Rmargin - strHeight) / 2
Y = TMargin + (PrintHeight - strWidth) / 2 e.Graphics.ResetTransform()
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
PRINTER AND PAGE PROPERTIES 715
e.Graphics.TranslateTransform(X, Y) e.Graphics.RotateTransform(90) e.Graphics.DrawString(RMarginCaption, pFont, Brushes.Black, 0, 0)
First we calculate the string’s width and height. The string will be rotated by 90 degrees before being printed. The rotation alone would place the string just outside the left margin, so we must translate it to the right. The amount of the translation is the page’s width minus half the difference between the string’s height and the right margin. Translating the caption by the width of the page would bring it to the very right edge of the paper. To center it in the right margin, we must split the difference of the string’s height from the right margin on either side of the string. We’re using the string’s height in calculating the X coordinate and the string’s width in calculating the Y coordinate, because after the string is rotated by 90 degrees, the width and height will be swapped. X and Y are the amounts by which the string must be moved along the horizontal and vertical axis. This movement will be performed by a translation transformation. The rotation of the string will be performed by a rotation transformation (for more information on transformations, see Chapter 14). Because transformations are cumulative, the code resets any existing transformations and applies two new ones. Then, the DrawString method is called to print the string. The DrawString method draws the string at the point (0, 0), but the two transformations will place it at the proper place.
The code for placing the other three captions is quite analogous. It uses the proper translation and rotation transformations, and the only complication is the calculation of the coordinates of the translation transformation. The listing of the PrintPage event handler of the PageSettings project is fairly lengthy, but it’s included in Listing 15.4 for your convenience.
Listing 15.4: Printing the Rectangle and the Margin Captions
Private Sub PrintDocument1_PrintPage(ByVal sender As Object, _
ByVal e As System.Drawing.Printing.PrintPageEventArgs) _ Handles PrintDocument1.PrintPage
Dim R As Rectangle
Dim strWidth, strHeight As Integer Dim pFont As Font
pFont = New Font(“Comic Sans MS”, 20) e.Graphics.DrawString(“ORIGIN”, pFont, Brushes.Black, 0, 0) pFont = New Font(“Comic Sans MS”, 40)
Dim X, Y As Integer
Dim TMarginCaption As String = “Top Margin String”
Dim LMarginCaption As String = “Left Margin String”
Dim RMarginCaption As String = “Right Margin String”
Dim BMarginCaption As String = “Bottom Margin String” Dim LMargin, RMargin, TMargin, BMargin As Integer With PrintDocument1.DefaultPageSettings.Margins
LMargin = .Left
RMargin = .Right
TMargin = .Top BMargin = .Bottom
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
716 Chapter 15 PRINTING WITH VB.NET
End With
Dim PrintWidth, PrintHeight, PageWidth, PageHeight As Integer With PrintDocument1.DefaultPageSettings.PaperSize
PrintWidth = .Width - LMargin - RMargin
PrintHeight = .Height - TMargin - BMargin PageWidth = .Width
PageHeight = .Height End With
If PrintDocument1.DefaultPageSettings.Landscape Then With PrintDocument1.DefaultPageSettings.PaperSize
PrintWidth = .Height - TMargin - BMargin
PrintHeight = .Width - RMargin - LMargin PageWidth = .Height
PageHeight = .Width End With
End If
‘ Draw rectangle
R = New Rectangle(LMargin, TMargin, PageWidth - LMargin - RMargin, _
PageHeight - BMargin - TMargin) e.Graphics.DrawRectangle(Pens.Black, R)
‘ Draw top margin’s caption
strWidth = e.Graphics.MeasureString(TMarginCaption, pFont).Width strHeight = e.Graphics.MeasureString(TMarginCaption, pFont).Height X = LMargin + (PrintWidth - strWidth) / 2
Y = (TMargin - strHeight) / 2 e.Graphics.TranslateTransform(X, Y)
e.Graphics.DrawString(TMarginCaption, pFont, Brushes.Black, 0, 0) ‘ Draw right margin’s caption
strWidth = e.Graphics.MeasureString(RMarginCaption, pFont).Width strHeight = e.Graphics.MeasureString(RMarginCaption, pFont).Height X = PageWidth - (RMargin - strHeight) / 2
Y = TMargin + (PrintHeight - strWidth) / 2 e.Graphics.ResetTransform() e.Graphics.TranslateTransform(X, Y) e.Graphics.RotateTransform(90)
e.Graphics.DrawString(RMarginCaption, pFont, Brushes.Black, 0, 0) ‘ Draw bottom margin’s caption
strWidth = e.Graphics.MeasureString(BMarginCaption, pFont).Width strHeight = e.Graphics.MeasureString(BMarginCaption, pFont).Height X = PageWidth - RMargin - (PrintWidth - strWidth) / 2
Y = PageHeight - (BMargin - strHeight) / 2 e.Graphics.ResetTransform() e.Graphics.TranslateTransform(X, Y) e.Graphics.RotateTransform(180)
e.Graphics.DrawString(BMarginCaption, pFont, Brushes.Black, 0, 0) ‘ Draw left margin’s caption
strWidth = e.Graphics.MeasureString(LMarginCaption, pFont).Width strHeight = e.Graphics.MeasureString(LMarginCaption, pFont).Height
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
PRINTER AND PAGE PROPERTIES 717
X = (LMargin - strHeight) / 2
Y = TMargin + (PrintHeight + strWidth) / 2 e.Graphics.ResetTransform() e.Graphics.TranslateTransform(X, Y) e.Graphics.RotateTransform(-90)
e.Graphics.DrawString(LMarginCaption, pFont, Brushes.Black, 0, 0) End Sub
As always, you must call the PrintDocument object’s Print method for this event handler to be activated. You can use the Print method of the PrintDocument object, but the project you’ll find on the CD uses the PrintPreviewDocument object to display a preview of the printout. Listing 15.5 shows the code behind the button on the form:
Listing 15.5: The Print Button
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Try
PrintPreviewDialog1.Document = PrintDocument1 PageSetupDialog1.PageSettings = PrintDocument1.DefaultPageSettings PageSetupDialog1.ShowDialog()
PrintDocument1.DefaultPageSettings = PageSetupDialog1.PageSettings PrintPreviewDialog1.ShowDialog()
Catch exc As Exception
MsgBox(“Printing Operation Failed” & vbCrLf & exc.Message) End Try
End Sub
The code uses an error handler to prevent the program from crashing with a runtime exception if there’s a problem with the printer. The application should work if there’s a default printer; it will fail to generate a preview only if the default printer is a network printer and you have no access to it at the time.
The first statement sets up the PrintPreview control by setting its Document property to the PrintDocument object. The second statement assigns the default page settings to the PageSetupDialog control and the following statement displays the Page Setup dialog box. Once the user has specified the desired settings and closes the dialog box, the new settings are assigned to the PrintDocument object’s DefaultPageSettings property. The last statement displays the Print Preview dialog box. This statement initiates the printing process, which sends its output to the preview pane, instead of the printer. That’s all it takes to add a preview feature to your application. The PrintPreviewDialog control is discussed later in this chapter.
If you feel uncomfortable with the transformations, especially the rotation transformation, Figure 15.6 shows what happens to a string when it’s rotated in all four directions around the origin. The origin—the point with coordinates (0, 0)—is where the two axes meet.
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |
718 Chapter 15 PRINTING WITH VB.NET
Figure 15.6
Rotating a string around the origin
The statements in Listing 15.6 rotate the string “GDI+ Graphics” around the origin by 90, 180, and 270 degrees. The numbers in parentheses indicate the angle of rotation for each string. Of course, I couldn’t print to the left of the origin, or above the origin, so the rotated strings were translated by 50 percent of the page’s width to the right and 50 percent of the page’s height down to appear at the middle of the page. The two axes were also translated by the same amounts in the two directions. This illustration’s purpose is to help you visualize how the string is rotated around the origin. Besides the string itself, the enclosing rectangle is also printed. This is the rectangle returned by the MeasureString method, subject to the same transformations as the string it encloses. Listing 15.6 shows the statements that generated the output shown in Figure 15.6. The project described here is the RotatedStrings project, and you will find it in this chapter’s folder on the CD.
Listing 15.6: Rotation and Translation Transformations
Private Sub PrintDocument1_PrintPage(ByVal sender As Object, _
ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
Handles PrintDocument1.PrintPage
Dim Tx, Ty As Integer
‘Tx, Ty are the coordinates of the center point on the page Tx = e.PageSettings.PaperSize.Width / 2
Ty = e.PageSettings.PaperSize.Height / 2
‘Draw a crosshair line at the middle of the page e.Graphics.DrawLine(New Pen(Color.Red, 2), Tx, 0, Tx, Ty * 2) e.Graphics.DrawLine(New Pen(Color.Red, 2), 0, Ty, Tx * 2, Ty) Dim fnt As Font = New Font(“Comic Sans MS”, 24, FontStyle.Bold) Dim RectSize As SizeF
Dim Rect As Rectangle
‘Print string without rotation
e.Graphics.TranslateTransform(Tx, Ty)
e.Graphics.DrawString(“GDI+ Graphics (0)”, fnt, Brushes.Black, 0, 0) RectSize = e.Graphics.MeasureString(“GDI+ Graphics (0)”, fnt)
Rect = New Rectangle(0, 0, RectSize.Width, RectSize.Height) e.Graphics.DrawRectangle(Pens.Green, Rect)
‘ Print string rotated 90 degrees
Copyright ©2002 SYBEX, Inc., Alameda, CA |
www.sybex.com |