So far, all the printing in this chapter has been done to the default printer, as defined by Windows itself. The user can change the printer for a document via the Printer button on the PageSetupDialog. It's more common, however, to allow the user to choose the printer after choosing the Print item from the File menu. For this you use the PrintDialog component, as shown in Figure 7.9. Figure 7.9. The PrintDialog Component
Using the PrintDialog component looks like this: Dim printDialog1 As PrintDialog Sub InitializeComponent() ... Me.printDialog1 = New PrintDialog() ... ' Can set the PrintDialog's Document property ' in the Property Browser Me.printDialog1.Document = Me.printDocument1 ... End Sub Sub printButton_Click(sender As Object, e As EventArgs) ' Let the user choose the printer If printDialog1.ShowDialog() = DialogResult.OK Then printDocument1.Print() End If End Sub Like PageSetupDialog, the PrintDialog component allows you to set a number of options before it is shown: NotInheritable Class PrintDialog Inherits CommonDialog Implements IComponent Implements IDisposable ' Constructors Public Sub New() ' Properties Property AllowPrintToFile() As Boolean Property AllowSelection() As Boolean Property AllowSomePages() As Boolean Property Document() As PrintDocument Property PrinterSettings() As PrinterSettings Property PrintToFile() As Boolean Property ShowHelp() As Boolean Property ShowNetwork() As Boolean ' Events Event HelpRequest As EventHandler ' Methods MustOverride Sub Reset() End Class You must set the Document property before showing a PrintDialog object. The other PrintDialog properties are similar in function to the PageSetupDialog properties. A couple of properties are special, however, because they determine what to print. Let's take a look. Print RangeThe AllowSelection property of the PrintDialog lets the user print only the current selection, [2] and AllowSomePages allows the user to decide on a subset of pages to be printed. Both settings require you to print specially, based on the PrintRange property of the PrinterSettings class, which is of type PrintRange:
Enum PrintRange AllPages ' Print all pages (default) Selection ' Print only the current selection SomePages ' Print pages from FromPage to ToPage End Enum Before you can set a print range that's different from AllPages, you must set AllowSelection or AllowSomePages (or both) to true (they both default to false). AllowSomePages also requires that the PrinterSettings FromPage and ToPage be set greater than the default of zero: Dim totalPages As Integer = 13 Dim page As Integer Dim maxPage As Integer Sub printButton_Click(sender As Object, e As EventArgs) printDocument1.PrinterSettings.FromPage = 1 printDocument1.PrinterSettings.ToPage = totalPages printDocument1.PrinterSettings.MinimumPage = 1 printDocument1.PrinterSettings.MaximumPage = totalPages printDialog1.AllowSomePages = True If printDialog1.ShowDialog() == DialogResult.OK Then printDocument1.Print() End If End Sub Although it's not required, it's a good idea when setting AllowSomePages to true to also set MinimumPage and MaximumPage so that users can't accidentally ask for a page out of the allowed range. If AllowSelection or AllowSomePages is set to true, the PrintPage event will have to check the PrintRange and FromPage/ToPage properties to see what to print: Dim totalPages As Integer = 13 Dim page As Integer Dim maxPage As Integer Sub printButton_Click(sender As Object, e As EventArgs) ... If printDialog1.ShowDialog() = DialogResult.OK Then If printDialog1.PrinterSettings.PrintRange = _ PrintRange.SomePages Then ' Set first page to print to FromPage page = printDocument1.PrinterSettings.FromPage ' Set last page to print to ToPage maxPage = printDocument1.PrinterSettings.ToPage Else ' Print all pages page = 1 maxPage = totalPages End If ' Print from first page to last page printDocument1.Print() End If End Sub Sub printDocument1_PrintPage(sender As Object, e As PrintPageEventArgs) Dim g As Graphics = e.Graphics ' print current page... ' Check whether there are more pages to print page += 1 e.HasMorePages = (page <= maxPage) End Sub In addition to the PrintRange, FromPage, and ToPage properties, the PrinterSettings class has many more settings for use in determining exactly how the user would like to print: Class PrinterSettings Implements ICloneable ' Constructors Public Sub New() ' Properties Property CanDuplex() As Boolean Property Collate() As Boolean Property Copies() As Short Property DefaultPageSettings() As PageSettings Property Duplex() As Duplex Property FromPage() As Integer Shared Property InstalledPrinters() As StringCollection Property IsDefaultPrinter() As Boolean Property IsPlotter() As Boolean Property IsValid() As Boolean Property LandscapeAngle() As Integer Property MaximumCopies() As Integer Property MaximumPage() As Integer Property MinimumPage() As Integer Property PaperSizes() As PaperSizeCollection Property PaperSources() As PaperSourceCollection Property PrinterName() As String Property PrinterResolutions() As PrinterResolutionCollection Property PrintRange() As PrintRange Property PrintToFile() As Boolean Property SupportsColor() As Boolean Property ToPage() As Integer ' Methods Function CreateMeasurementGraphics() As Graphics End Class One thing of particular interest is the CreateMeasurementGraphics method, which returns a Graphics object based on the printer and its settings. You can use this Graphics object for making measurement calculations and for enumerating the font families (using the FontFamily.GetFamilies method), all without having to actually start a print operation. Targeting the PrinterI'd like to remind you again that because the drawing happens on a Graphics object, all the drawing techniques from Chapters 4, 5, and 6 work just as well with printers as they do with screens. However, unlike the screen, where page units default to Pixel, the page units for the printer default to Display. Furthermore, whereas Display means Pixel on the screen, for the printer, Display maps the printer resolution to a logical 100 dpi. Because printers often have different resolutions both vertically and horizontally and are almost never 100 dpi anyway, this may seem unintuitive. However, because the default system font setting is 96 dpi on the screen, mapping the printer to a logical 100 dpi means that the default mappings for both screen and printer yield a quick and dirty near-WYSIWYG, without your having to change a thing. If you want something even closer, you're free to use page units such as inches or millimeters, as discussed in Chapter 6: Advanced Drawing. If you do change the units, remember to convert PageBounds and MarginBounds to the new units as well. You can use the Graphics method TransformPoints: Shared Function TranslateBounds(g As Graphics, _ bounds As Rectangle) As RectangleF ' Translate from units of 1/100th of an inch to page units Dim dpiX As Single = g.DpiX Dim dpiY As Single = g.DpiY Dim pts(1) As PointF pts(0) = _ New PointF(bounds.X * dpiX / 100.0F, _ bounds.Y * dpiY / 100.0F) Pts(1) = _ New PointF(bounds.Width * dpiX / 100.0F, _ bounds.Height * dpiX / 100.0F) g.TransformPoints( _ CoordinateSpace.Page, CoordinateSpace.Device, pts) Return New RectangleF(pts(0).X, pts(0).Y, pts(1).X, pts(1).Y) End Function The TranslateBounds helper method uses the current Graphics object to translate a PageBounds or MarginBounds rectangle from units of 100 dpi to whatever the page unit is set to. This helper is meant to be used from the PrintPage handler: Sub printDocument1_PrintPage(sender As Object, e As PrintPageEventArgs) Dim g As Graphics = e.Graphics g.PageUnit = GraphicsUnit.Inch Dim thinPen As Pen = New Pen(Color.Black, 0) Dim pageBounds As RectangleF = GetRealPageBounds(e, preview) pageBounds = TranslateBounds(g, Rectangle.Truncate(pageBounds)) g.DrawRectangle(_ thinPen, pageBounds.X, pageBounds.Y, pageBounds.Width, pageBounds.Height) ... End Sub Notice that PageUnit is set on the Graphics object right away so that any drawing that takes place in the PrintPage handler will be in those units. Notice also the creation of a new Pen object with a thickness of zero. By default, all the pens exposed from the Pens class have a width of 1, which will be one unit thick, or, in this example, one inch thick. A pen of width zero, on the other hand, will always be one device unit thick, something that is more useful for framing a rectangle. Finally, notice that the PrintPage handler sets the PageUnit during each page being printed. Each time the PrintPage handler is called, it gets a fresh Graphics object, so don't forget to reset its options every time. |