Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
120
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

PRINTING EXAMPLES 733

rectangle as possible, even if the last line fits only partially in the rectangle. To force the MeasureString and DrawString methods to work with an integer number of lines, create a FormatString object passing the constant StringFormatFlags.LineLimit as argument:

Dim fmt As New StringFormat(StringFormatFlags.LineLimit)

If you pass the fmt object as argument to both the MeasureString and DrawString methods, they will ignore partial lines and the rest of the printing code works as expected.

If the user changes the orientation of the page, the code switches the page’s width and height. This is all it takes to print text in landscape orientation. The page’s margins are also accounted for.

Before ending this section, I should explain why I haven’t started with this version of the application (which is also simpler that the PrintText project of the previous section). The PrintText2 application prints one page at a time. You have no control over the individual lines or words. The granularity of the PrintText2 application is an entire page. If the text contains formatting information (simple tags to turn on and off the attributes like bold and italics), you won’t be able to process them. Assume that the text contains HTML-like tags (like the <B> and <I> tags) to determine the appearance of the text, or custom tags to specify the formatting of section headers. When you treat a block of text as a single entity, you won’t be able to process individual words.

The same is true for simpler types of processing. Let’s say you want to format a program listing by inserting a continuation symbol at the end of every code line that has to be broken into two or more text lines. Since we rely on the DrawString method to break long code lines into multiple text lines for us, we can’t insert the appropriate symbols, or indent the continued lines, as is customary in program listings. It’s also common to number the lines of program listings. Even this simple operation can’t be incorporated into the PrintText2 application’s project. So a text-printing application that prepares the page one word at a time may be slow, but it’s flexible, and you’re more likely to write code based on the PrintText sample application. The technique demonstrated by the PrintText2 application is a convenient method of printing simple text files, similar to printing them with Notepad.

Printing Bitmaps

If you have a color printer, you probably want to print images, too. Printing bitmaps is quite simple. As you have probably guessed, you call the DrawImage method to send the bitmap to the printer. As a reminder, the simplest form of the DrawImage method of the Graphics object accepts two arguments, which are the bitmap to be drawn (an Image object) and a rectangle in which the image will be drawn:

Graphics.DrawImage(image, rectangle)

The method will stretch the bitmap specified by the image argument to fill the rectangle specified by the second argument. Because of this, it is imperative that you calculate carefully the dimensions of the rectangle, so that they will retain their original aspect ratio. If not, the image will be distorted in the process. Most applications will let the user specify a zoom factor, which is then applied to both dimensions. If the image fits on the page, you can make the rectangle equal to the dimensions of the image and not worry about distortions.

Since the reduced image will, most likely, be smaller than the dimensions of the paper on which it will be printed, you must also center the image on the paper. To do so, you can subtract the image’s width from the paper’s width and split the difference on the two sides of the image (you will do the same for the vertical margins).

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

734 Chapter 15 PRINTING WITH VB.NET

If you specify a rectangle the same size as the image, the image will be printed in its actual size. A common image resolution is 72 dots per inch. If the bitmap is 1,024 pixels wide, it will take approximately 14 inches across the page—this means that part of the image won’t be printed.

Figure 15.10

The PrintBitmap application resizes bitmaps to fit the width of the page and prints them.

Before you send a bitmap to the printer, you must first make sure the bitmap will fit on the page. If the bitmap is too large for a letter-size page, you must reduce its size. Fortunately, the Framework can handle this for you. All you have to do is specify the size of the rectangle on the paper, in which the image will be printed. The following statements, which must appear in the PrintDocument event, print the image centered on the page. If the image doesn’t fit on the page, its top-left corner is printed at the origin, and only the rightmost and bottommost parts of the image will be missing. Notice also that the image isn’t printed in actual size; instead, it’s printed at the current magnification. Listing 15.13 provides the code of the PrintPage event handler.

Listing 15.13: Scaling and Printing a Bitmap

Private Sub PrintDocument1_PrintPage(ByVal sender As Object, _

ByVal e As System.Drawing.Printing.PrintPageEventArgs) _

Handles PrintDocument1.PrintPage

Dim R As Rectangle

Dim PictWidth, PictHeight, PictLeft, PictTop As Integer

PictWidth = PictureBox1.Width

PictHeight = PictureBox1.Height

With PrintDocument1.DefaultPageSettings.PaperSize

If PictWidth < .Width Then

PictLeft = (.Width - PWidth) / 2

Else

PictLeft = 0

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

PRINTING EXAMPLES 735

End If

If PictHeight < .Height Then PictTop = (.Height - PHeight) / 2

Else

PictTop = 0 End If

End With

R = New Rectangle(PictLeft, PictTop, PictWidth, PictHeight) e.Graphics.DrawImage(PictureBox1.Image, R)

End Sub

The PictWidth and PictHeight variables hold the dimensions of the scaled image, while PictLeft and PictTop are the coordinates of the image’s top-left corner on the page. To initiate the printing process, you must call the PrintDocument object’s Print method, or you can display the PrintPreview dialog box, which is what the following code does:

Private Sub bttnPrint_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button2.Click

PrintPreviewDialog1.Document = PrintDocument1

PrintPreviewDialog1.ShowDialog()

End Sub

The user can resize and rotate the image before printing it. These rotation commands can be found in the main form’s Process menu, while the Zoom menu has four options: Auto, Normal, Zoom In, and Zoom Out (Figure 15.11). The last two commands zoom in and out by 25 percent at a time. These commands change the size of the PictureBox control that holds the image, and the PrintPage event handler uses the dimensions of this control to determine the dimensions of the printed image. The Normal command resets the image to its actual size, and the Auto command resizes the image proportionally so that its height is 400 pixels.

Figure 15.11

The PrintBitmap application’s main form

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

736 Chapter 15 PRINTING WITH VB.NET

Using the PrintPreviewControl

The PrintPreviewControl is the core of the PrintPreviewDialog control packaged as a Windows control. It consists of the preview pane, where the printed document can be previewed. Other than that, it has a vertical and horizontal scroll bar but no controls to zoom or to move from

page to page. You can use this control to create a custom print-preview form for specialized applications, but you must design an interface that will allow users to navigate through the document being viewed. You should use the PrintPreviewDialog control instead, but if you want to use a different interface or restrict the preview pane on a form, you’ll find all the information you need to program the PrintPreviewControl in this section.

Just like its cousin, the PrintPreviewDialog, the PrintPreviewControl requires the presence of

a PrintDocument control. This control will provide the document to be viewed and will expose the PrintPage event, where all the action takes place. Once you’ve added a PrintPreviewControl and a PrintDocument control to the project, you can assign the instance of the PrintDocument control to the Document property of the PrintPreviewControl with a statement like the following, and you’re ready to preview:

PrintPreviewControl1.Document = PrintDocument1

Unlike the PrintPreviewDialog control, the PrintPreviewControl is visible at runtime. Not only that, but you need quite a bit of space on your form for this control to work. Figure 15.12 shows a form with a TextBox control on the left and a PrintPreviewControl on the right. The interface for previewing the document is quite trivial, but it demonstrates the basic properties of the control.

We’ll get to the interface shortly.

First, size the control on the form. You should probably dock it on the edges of the form, so that users can control the preview pane’s size. Then place the Preview button on the form and enter the following code in its Click event handler:

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

PrintPreviewControl1.Columns = 2

PrintPreviewControl1.Document = PrintDocument1

End Sub

The Columns property determines how many pages appear next to each other on the control. There’s also a Rows property, which determines how many rows of pages are on the control. To display four pages at a time on the control, set both properties to 2.

After setting the control’s Document property, you can insert the code to generate the printout in the PrintDocument control’s PrintPage event handler. The output will be sent to the Preview control, not to the printer, or even to the PrintPreview dialog box. In this example we’ll print eight pages, so that you can experiment with the various settings of the control. We’ll print the text on the TextBox control on eight pages (all pages will display the same text). Each page will be differentiated by the page number, which will appear in bold at the top of the page, as shown in Figure 15.12. Listing 15.14 shows the code in the PrintPage event handler.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

PRINTING EXAMPLES 737

Listing 15.14: Printing a Simple Document to the PrintPreview Control

Private Sub PrintDocument1_PrintPage(ByVal sender As Object, _

ByVal e As System.Drawing.Printing.PrintPageEventArgs) _ Handles PrintDocument1.PrintPage

Static iPage As Integer

e.Graphics.DrawString(“PAGE # “ & (iPage+ 1).ToString, _

New Font(“Comic sans MS”, 24), Brushes.Black, 10, 10) e.Graphics.DrawString(RichTextBox1.Text, RichTextBox1.Font, _

Brushes.Black, 50, 50)

iPage = iPage + 1 If iPage = 8 Then

e.HasMorePages = False Else

e.HasMorePages = True End If

End Sub

Figure 15.12

Previewing a simple document on the PrintPreview control

The static variable iPage is increased each time a new page is printed, and while it’s less than 8, the HasMorePages property is set to True, to continue printing. If you run the project at this point, you will see the first two pages on the PrintPreviewControl, but you have no way to zoom or jump to the following pages. The default magnification is set automatically, so that you can view the width of a page. To change the default magnification, you must set the Zoom property to the appropriate value. A value of 1 displays the document in actual size. The magnification you see will be something close to 0.3.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

738 Chapter 15 PRINTING WITH VB.NET

The Zoom In and Zoom Out buttons control the magnification. The Zoom In button increases the magnification by increasing the Zoom property by 25 percent. If the zoom value is more than 3 (three times the actual size), the Zoom property is clipped to 3.

Private Sub Button2_Click(ByVal sender As Object, _

ByVal e As System.EventArgs) Handles Button2.Click

PrintPreviewControl1.Zoom = PrintPreviewControl1.Zoom * 1.25

If PrintPreviewControl1.Zoom > 3 Then PrintPreviewControl1.Zoom = 3

End Sub

Similarly, the Zoom Out button decreases the Zoom factor by 25 percent each time, down to a smallest magnification of 0.3:

Private Sub Button3_Click(ByVal sender As Object, _

ByVal e As System.EventArgs) Handles Button3.Click

PrintPreviewControl1.Zoom = PrintPreviewControl1.Zoom / 1.25

If PrintPreviewControl1.Zoom < 0.3 Then PrintPreviewControl1.Zoom = 0.3

End Sub

The other two buttons allow you to move forward and backward through the pages. The current page on the preview pane is set by the StartPage property, so the other two buttons control the StartPage property. If the control displays multiple pages, the Page property is the number of the first page on the control. Our control displays two pages, so the StartPage property is increased or decreased by 2 (which is the value of the Columns property). If you set up a PrintPreview control with pages in multiple rows and columns, use the product of the Columns property times the Rows property. Here’s the code behind the two navigational buttons:

Private Sub bttnNext_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnNext.Click

PrintPreviewControl1.StartPage = _

PrintPreviewControl1.StartPage + PrintPreviewControl1.Columns

End Sub

Private Sub bttnPrevious_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnPrevious.Click

PrintPreviewControl1.StartPage = _

PrintPreviewControl1.StartPage - PrintPreviewControl1.Columns

End Sub

You may have noticed that the code doesn’t check the current value of the StartPage property. Even if you attempt to set this property to an invalid value, no exception will be thrown. The reason for this behavior is that we never know the number of the last page, so the control itself takes care of possible erroneous settings of the StartPage property.

The PrintPreview control exposes the UseAntiAlias property, which is a True/False value that indicates whether the control will use anti-aliasing in rendering the text. Set this property to True for the best possible preview, since the resolution of the screen (100 pixels or so per inch) is much lower than the resolution of a typical printer (600 to 1,200 dots per inch).

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com