Printing with Windows Forms

Important Printing Classes:
PrintDocument

PrintDocument is used to send output to a printer. You instantiate a PrintDocument, set some properties describing what to print, and call the Print method. The PrintDocument raises a PrintPage event for each page to be printed. You add your printing logic to an event handler for this event.

PrinterSettings

Information about how a document should be printed. This includes which printer to print it on.

PageSettings

Information about how a page should be printed.

PrintPageEventArgs

The data for the PrintPage event on PrintDocument. It provides a clipping rectangle and a Graphics object for the printing surface.

PrintEventArgs

The data for the BeginPrint and EndPrint events on PrintDocument. Allows the print job to be cancelled.

PrintDialog

Printer selection dialog. Wraps the Win32 PrintDlg API.

PageSetupDialog

Page properties dialog. Wraps the Win32 PageSetupDlg API.

PrintPreviewControl

A control that displays a PrintDocument. Allows the creation of a print preview dialog.

PrintPreviewDialog

A dialog that displays a PrintDocument by using the PrintPreviewControl.

PrintController

A PrintController controls how a PrintDocument is printed. PrintDocument.Print uses a print control to render the document.

The .NET Framework provides two print controllers:

  • DefaultPrintController renders to a printer.
  • PreviewPrintController renders to the PrintPreviewControl.

Typically, you never have to implement a PrintController. You implement a PrintController only if you want to render to a different surface.

Building an Application that Prints

The easiest way to understand how the printing classes fit together is to work through a simple example that prints the contents of a text file to a printer.

Step 1: Print Logic

The first thing you need to do is to write your printing logic. You do this by handling events on a PrintDocument. When the PrintDocument.Print() method is called, the following events are raised:

  • BeginPrint
  • PrintPage (one or more)
  • EndPrint

The PrintPage event arguments type (PrintPageEventArgs) has a HasMorePages property. If this is set to true when your event handler returns, PrintDocument sets up a new page and raises the PrintPage event again.

Therefore, the logic in your PrintPage event handler is basically the following:

  • Print the contents of the page using the information in the event arguments. The event arguments contain the Graphics for the printer, the PageSettings for that page, the bounds of the page, and the size of the margins.
  • Determine whether there are more pages to print.
  • If there are more pages, set HasMorePages to true.
  • If there are not more pages, set HasMorePages to false.

In the simplest case, you can create a PrintDocument and handle the PrintPage event as part of the Form that contains the print request.


Public Class PrintingExample1
    Inherits System.Windows.Forms.Form

    ....

    private printFont As Font
    private streamToPrint As StreamReader

    Public Sub New ()
        MyBase.New
        InitializeComponent()
    End Sub

    'Event fired when the user presses the print button
    Private Sub printButton_Click(sender As object, e As System.EventArgs)

        Try
            streamToPrint = new StreamReader ("PrintMe.Txt")
            Try
                printFont = new Font("Arial", 10)
                Dim pd as PrintDocument = new PrintDocument() 'Assumes the default printer
                AddHandler pd.PrintPage, AddressOf Me.pd_PrintPage
                pd.Print()
            Finally
                streamToPrint.Close()
            End Try

        Catch ex As Exception
            MessageBox.Show("An error occurred printing the file - " + ex.Message)
        End Try

    End Sub

    'Event fired for each page to print
    Private Sub pd_PrintPage(sender As object, ev As System.Drawing.Printing.PrintPageEventArgs)

        Dim lpp As Single = 0
        Dim yPos As Single =  0
        Dim count As Integer = 0
        Dim leftMargin As Single = ev.MarginBounds.Left
        Dim topMargin As Single = ev.MarginBounds.Top
        Dim line as String

        'Work out the number of lines per page
        'Use the MarginBounds on the event to do this
        lpp = ev.MarginBounds.Height  / printFont.GetHeight(ev.Graphics)

        'Now iterate over the file printing out each line
        'NOTE WELL: This assumes that a single line is not wider than the page width
        'Check count first so that we don't read line that we won't print
        line=streamToPrint.ReadLine()
        while (count < lpp AND line <> Nothing)

            yPos = topMargin + (count * printFont.GetHeight(ev.Graphics))

            'Print Preview control will not work.
            ev.Graphics.DrawString (line, printFont, Brushes.Black, leftMargin, _
                                    yPos, new StringFormat())

            count = count + 1

            if (count < lpp) then
                line=streamToPrint.ReadLine()
            end if

        End While

        'If we have more lines then print another page
        If (line <> Nothing) Then
            ev.HasMorePages = True
        Else
            ev.HasMorePages = False
        End If

    End Sub

    ....

End Class
VB

To view and run this sample.

 
VB Simple Printing Example1

[Run Sample] | [View Source]

Step 2: Defining Your Own Print Document

For a complex print job, or a print logic that you wish to reuse across multiple forms, you can derive a new class from PrintDocument and encapsulate your printing logic in that class. In this case, you handle the PrintPage event by overriding the OnPrintPage method, rather than using an event handler.



Public Class TextFilePrintDocument
    Inherits PrintDocument

    Private printFont As Font
    Private streamToPrint As StreamReader

    Public Sub New(streamToPrint As StreamReader)
        MyBase.New
        Me.streamToPrint = streamToPrint
    End Sub

    'Override OnBeginPrint to set up the font we are going to use
    Overrides Protected Sub OnBeginPrint(ev As PrintEventArgs)
        MyBase.OnBeginPrint(ev)
        printFont = new Font("Arial", 10)
    End Sub

    'Override the OnPrintPage to provide the printing logic for the document
    Overrides Protected Sub OnPrintPage(ev As PrintPageEventArgs)

        MyBase.OnPrintPage(ev)

        Dim lpp As Single = 0
        Dim yPos As Single =  0
        Dim count As Integer = 0
        Dim leftMargin As Single = ev.MarginBounds.Left
        Dim topMargin As Single = ev.MarginBounds.Top
        Dim line as String

        'Work out the number of lines per page
        'Use the MarginBounds on the event to do this
        lpp = ev.MarginBounds.Height / printFont.GetHeight(ev.Graphics)

        'Now iterate over the file printing out each line
        'NOTE WELL: This assumes that a single line is not wider than the page width
        'Check count first so that we don't read line that we won't print
        line = streamToPrint.ReadLine()
        While (count < lpp And line <> Nothing)

            yPos = topMargin + (count * printFont.GetHeight(ev.Graphics))

            ev.Graphics.DrawString(line, printFont, Brushes.Black, leftMargin, _
                                   yPos, New StringFormat())

            count = count + 1

            If (count < lpp) Then
                line = streamToPrint.ReadLine()
            End If

        End While

        'If we have more lines then print another page
        If (line <> Nothing) Then
            ev.HasMorePages = True
        Else
            ev.HasMorePages = False
        End If

    End Sub

End Class
VB

To view and run this sample.

 
VB Simple Printing Example2

[Run Sample] | [View Source]

Step 3: Allowing the User to Choose a Printer

When you have your print logic working, the next step is to allow the user to choose a printer with the Windows Print dialog. To do this, you create a PrintDocument and pass it to the PrintDialog.



Private Sub printButton_Click(ByVal sender As Object, ByVal e As System.EventArgs)

    Try
        Dim streamToPrint As StreamReader = New StreamReader("PrintMe.Txt")
        Try
            'Assumes the default printer
            Dim pd As TextFilePrintDocument = New TextFilePrintDocument(streamToPrint)

            Dim dlg As New PrintDialog()
            dlg.Document = pd
            Dim result As DialogResult = dlg.ShowDialog()

            If (result = System.Windows.Forms.DialogResult.OK) Then
                pd.Print()
            End If

        Finally
            streamToPrint.Close()
        End Try

    Catch ex As Exception
        MessageBox.Show("An error occurred printing the file - " + ex.Message)
    End Try

End Sub
VB

To view and run this sample.

 
VB Simple Printing Example3

[Run Sample] | [View Source]

Step 4: Allowing the User to Choose Page Settings

Once the user can choose a printer and print a document, you can allow them to choose page settings, such as the orientation of the paper or the size of the margins. To do this, you create a PageSettings instance and pass it to the PageSetupDialog.



'Event fired when the user presses the page setup button
Private Sub pageSetupButton_Click(sender As object, e As System.EventArgs)

    Try
        Dim psDlg As New PageSetupDialog

        If (storedPageSettings Is Nothing) Then
            storedPageSettings = new PageSettings()
        End If

        psDlg.PageSettings = storedPageSettings
        psDlg.ShowDialog
    Catch ex As Exception
        MessageBox.Show("An error occurred - " + ex.Message)
    End Try

End Sub
VB

You can then use this custom PageSettings instance when the user prints the document.



'Event fired when the user presses the print button
Private Sub printButton_Click(sender As object, e As System.EventArgs)

    Try
        Dim streamToPrint As StreamReader = new StreamReader ("PrintMe.Txt")
        Try
            'Assumes the default printer
            Dim pd As TextFilePrintDocument = new TextFilePrintDocument(streamToPrint)

            If Not (storedPageSettings Is Nothing) Then
                pd.DefaultPageSettings = storedPageSettings
            End If


            Dim dlg As New PrintDialog()
            dlg.Document = pd
            Dim result As DialogResult = dlg.ShowDialog()

            If (result = System.Windows.Forms.DialogResult.OK) Then
                pd.Print()
            End If

        Finally
            streamToPrint.Close()
        End Try

    Catch ex As Exception
        MessageBox.Show("An error occurred printing the file - " + ex.Message)
    End Try

End Sub
VB

To view and run this sample.

 
VB Simple Printing Example4

[Run Sample] | [View Source]

Step 5: Displaying a Print Preview Window

The Print Preview window allows a user to preview their document before printing it. You can add a Print Preview window to your application by creating a PrintDocument and passing it to the PrintPreview dialog.



'Event fired when the user presses the print preview button
Private Sub printPreviewButton_Click(sender As object, e As System.EventArgs)

    Try
        Dim streamToPrint As StreamReader = new StreamReader ("PrintMe.Txt")
        Try
            'Assumes the default printer
            Dim pd As TextFilePrintDocument = new TextFilePrintDocument(streamToPrint)

            If Not (storedPageSettings Is Nothing) Then
                pd.DefaultPageSettings = storedPageSettings
            End If

            Dim dlg As New PrintPreviewDialog()
            dlg.Document = pd
            dlg.ShowDialog()

        Finally
            streamToPrint.Close()
        End Try

    Catch ex As Exception
        MessageBox.Show("An error occurred - " + ex.Message)
    End Try

End Sub
VB

To view and run this sample.

 
VB Simple Printing Example5

[Run Sample] | [View Source]

Putting it All Together

The Windows Forms samples include a sample called SimplePad that shows how to build a simple text editor that prints the file that is being edited.


 
C# SimplePad

[Run Sample] | [View Source]


Copyright 2001 Microsoft Corporation. All rights reserved.