A beginner's guide to printing in .NET-VBForums
Results 1 to 7 of 7

Thread: A beginner's guide to printing in .NET

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2002
    Dublin, Ireland

    A beginner's guide to printing in .NET

    It all starts with a blank sheet of paper
    The printing subsystem can be particularly daunting the first time it is encountered. It is unfamiliar to most developers, and it lacks the draw-and-code experience that the Windows Forms Designer has made so familiar to us. However there are a number of classes built in to the .NET framework that can make the whole experience a bit less of a chore...

    The print system
    Although it is possible to live a happy and fulfilling life without ever looking under the bonnet at the print subsystem, it does make it slightly easier to write an application that has hardcopy capabilities if you have some understanding of what the Operating System does in response to the “print” command. The first thing an application needs to know in order to print is what print device to print to. To achieve this, it queries the spool server and asks it to return a list of the devices attached, and some basic information about them (name, location, device driver etc.). Then, the user selects one, and the application obtains a handle that refers to that printer, through which it queries the device settings (such as the paper size it uses, resolution etc.), and then it obtains a device context on which to draw the first page. Drawing the page is done with the standard GDI drawing commands such as ExtTextOut, Rectangle, Polygon etc. Once the page is drawn, the application either notifies the spool system that the job is complete, or requests another page.

    At this stage, the drawn page can be translated into the printer control language appropriate to the printer by the printer driver, and the hardware can do its thing. In between pages, the application can also make changes to the settings of the printer so that, for example, one page can be printed landscape and the next portrait etc. Whenever the application or the printer performs an action on the print job, a notification is raised so that any application which monitors print jobs can be notified of their progress and so that any problems that occur printing the document can be notified to the user to rectify them.

    Printing in .NET

    Printing in .NET follows the overview of the print system quite closely. There are two main framework classes that you use to do all your work with printing, which are in the System.Drawing.Printing namespace: PageSettings, which is used for such things as selecting the paper size and page orientation, and PrintDocument, which is used to do the printing operation itself.

    Arguably, the best way of implementing a custom print operation in .NET is to create your own class that holds a private variable that is of type PrintDocument and write the code to perform the printing in the event handlers of that class to print the desired document. I will discuss these in the order in which they occur in the life of a print operation.

    The BeginPrint event is raised when the print job is initiated. You should use this event to do any setting up that will be required to perform the print job: set the data pointers (if any) to the start position, and initialise any page number variables you might have.
    VB Code:
    1. Private Sub PrintDocument_Form_BeginPrint(ByVal sender As Object, _
    2.   ByVal e As System.Drawing.Printing.PrintEventArgs)_
    3.    Handles PrintDocument_Form.BeginPrint

    The QueryPageSettings event is raised before each and every page that is printed. You use this event to set any page settings for the next page to be printed. You can set the orientation (landscape or portrait), the paper size, paper source, and so on.

    In this example, we want the third page in landscape:
    VB Code:
    1. Private Sub PrintDocument_Form_QueryPageSettings(ByVal sender As Object, _
    2.      ByVal e As System.Drawing.Printing.QueryPageSettingsEventArgs) _
    3.      Handles PrintDocument_Form.QueryPageSettings
    5.     If _LogicalPageNumber = 3 Then
    6.       e.PageSettings.Landscape = True
    7.     Else
    8.       e.PageSettings.Landscape = False
    9.    End If
    10.  End Sub

    All of the actual printing on a page is done in the PrintPage event handler. This event is raised for each page that is printed, and the drawing and text commands that you code in this event handler control what is printed on the page itself.

    VB Code:
    1. Private Sub PrintDocument_Form_PrintPage(ByVal sender As Object, _
    2.     ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
    3.     Handles PrintDocument_Form.PrintPage

    At the end of each page, you decide if there are more pages to print, set the e.HasMorePages property to True, and the QueryPageSettings and PrintPage events will be triggered again. It is important to remember that the same event handler is used, so if you want to print different pages, you need to keep track of which page you are on and code your PrintPage event handler accordingly.

    When the last page is printed (and you have told the print system that there are no more pages to follow), the EndPrint event is raised. You can use this event handler to clear up any objects that you have created for the print job.

  2. #2

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2002
    Dublin, Ireland

    Re: A beginner's guide to printing in .NET

    Hints and tips

    • Graphics measurements in printing are in tenths of a millimeter - e.g., to print a rectangle 4 x 5 centimeters, you would use e.Graphics.DrawRectangle(Pens.Black, 10, 10, 4000, 5000)
    • Z-Order (such as it is) is defined by the order that the print commands are issued - i.e., later drawing commands print on top of earlier ones

    Worked example
    To illustrate this, the following is a "quick and dirty" worked example that is a restaurant guide. The data source for this is an XML data set thus:-

    <?xml version="1.0" encoding="utf-8" ?>
    <!-- The list of restaurants -->
    <RestaurantMenu xmlns="http://tempuri.org/RestaurantMenu.xsd">
    <Name>MV Cillairne</Name>
    <Address_1 > North Wall Quay </Address_1>
    <Address_2 > Docklands, Dublin 1 </Address_2>
    <Logo_Image >D:\Users\Duncan\Documents\Presentations\
    Hardcore Hardcopy\RestaurantMenuPrinter\RestaurantMenuPrinter\

    To print out this data, we create a new class that inherits from PrintDocument:
    VB Code:
    1. Public Class RestaurantDocument
    2.        Inherits System.Drawing.Printing.PrintDocument

    To print this record set, we need a record pointer (to know where we are in the data set) and a couple of fonts to print the data with. These are going to be set up in the BeginPrint event and used in the PrintPage event, so are declared at class level:
    VB Code:
    1. Private _restaurantDataSet As DataSet
    2.      Private _currentPage As Integer = 0
    3.     Private _currentRecord As Integer = 0
    4.      Private _titleFont As Font
    5.     Private _detailFont As Font
    and are initialised in the BeginPrint event:
    VB Code:
    1. ''' <summary />
    2.     ''' The BeginPrint event is raised when the print job is initiated.
    3.     ''' You should use this event to do any setting up that will be
    4.      ''' required to perform the print job
    5.     ''' </summary />
    6.    Private Sub RestaurantDocument_BeginPrint(ByVal sender As Object, _
    7.               ByVal e As System.Drawing.Printing.PrintEventArgs) _
    8.                      Handles Me.BeginPrint
    9.          '\\ Create the font objects we are going to print with
    10.         _titleFont = New Font(FontFamily.GenericSerif, 16, _
    11.                          FontStyle.Bold, GraphicsUnit.Point)
    12.         _detailFont = New Font(FontFamily.GenericSerif, 9, _
    13.                            FontStyle.Regular, GraphicsUnit.Point)
    14.      End Sub

    For each page in the restaurant guide, we print the restaurant name, logo, and address. Text elements are printed using the Graphics.DrawString command, and images using the Graphics.DrawImage command:
    VB Code:
    1. ''' <summary />
    2.     ''' The PrintPage event is called once for each page to print
    3.     ''' </summary />
    4.     ''' <remarks />
    5.     ''' Set e.HasMorePages to true to print more pages
    6.     ''' </remarks />
    7.     Private Sub RestaurantDocument_PrintPage(ByVal sender As Object, _
    8.                  ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
    9.                       Handles Me.PrintPage
    10.          With _restaurantDataSet.Tables(0).Rows(_currentRecord)
    11.             '\\ Print the restaurant name
    12.             e.Graphics.DrawString(.Item(&quot;Name&quot;).ToString, _
    13.                         _titleFont, Brushes.Black, 20, 20)
    14.              Dim yPos As Single = 100
    15.              '\\ Print the restaurant image
    16.             If (.Item("Logo_Image").ToString <> "") Then
    17.                 Dim fiImage As New _
    18.                     System.IO.FileInfo(.Item("Logo_Image").ToString)
    19.                 Dim img As New Bitmap(fiImage.FullName)
    20.                 e.Graphics.DrawImage(img, 20, 60)
    21.                  yPos += img.Height
    22.             End If
    24.              '\\ Print the restaurant address details
    25.             e.Graphics.DrawString(.Item("Address_1").ToString, _
    26.                          _detailFont, Brushes.Black, 20, yPos)
    27.             yPos += e.Graphics.MeasureString(.Item("Address_1").ToString, _
    28.                         _detailFont).Height
    30.             e.Graphics.DrawString(.Item("Address_2").ToString, _
    31.                          _detailFont, Brushes.Black, 20, yPos)
    33.             yPos += e.Graphics.MeasureString(.Item("Address_2").ToString,  _
    34.                         _detailFont).Height
    36.             e.Graphics.DrawString(.Item("Telephone").ToString, _
    37.                         _detailFont, Brushes.Black, 20, yPos)
    38.           End With
    40.         _currentRecord += 1
    42.          If (_currentRecord < _restaurantDataSet.Tables(0).Rows.Count) Then
    43.             e.HasMorePages = True
    44.             _currentPage += 1
    45.         End If
    46.      End Sub
    And, Graphics.MeasureString is used to correctly space the address lines underneath each other.

    Hooking the print up to a Windows form
    In order to print and print preview this rudimentary document, we need to hook it up to a Windows form which has a PrintDialog and a PrintPreviewDialog component on it and three menu items: Print Preview, Print, and also Document Properties.
    The code to tie these three elements together is based on having a private instance of the RestaurantDocument class in the form code:
    VB Code:
    1. Public Class Form1
    2.      Private MyRestaurantDoc As RestaurantDocument
    3.     '--8<--------------

    To change the document settings, we show the PrintDialog dialog box:
    VB Code:
    1. Private Sub DocumentSettingsToolStripMenuItem_Click(ByVal sender As Object, _
    2.             ByVal e As System.EventArgs) _
    3.                Handles DocumentSettingsToolStripMenuItem.Click
    4.          If Me.PrintDialog_Restaurant.ShowDialog = Windows.Forms.DialogResult.OK Then
    5.             MyRestaurantDoc.PrinterSettings = Me.PrintDialog_Restaurant.PrinterSettings
    6.         End If
    7.      End Sub

    To preview the document, we need to show the PrintPreviewDialog component dialog box:
    VB Code:
    1. Private Sub PrintPreviewDialog_Restaurant_Click(ByVal sender As Object, _
    2.                      ByVal e As System.EventArgs) _
    3.                        Handles PrintPreviewToolStripMenuItem.Click
    4.         PrintPreviewDialog_Restaurant.Document = MyRestaurantDoc        PrintPreviewDialog_Restaurant.ShowDialog()
    5.     End Sub
    and lastly, (and indeed most simple of all) to print the document to a printer:
    VB Code:
    1. Private Sub PrintToolStripMenuItem_Click(ByVal sender As Object, _
    2.                  ByVal e As System.EventArgs) _
    3.                     Handles PrintToolStripMenuItem.Click
    4.         MyRestaurantDoc.Print()
    5.     End Sub

    I hope this article gives you enough insight to allow you to approach the .NET printing subsystem. Once you do, you will find it is a very powerful part of the framework which will help in producing more fully featured Windows applications.

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2002
    Dublin, Ireland

    Re: A beginner's guide to printing in .NET

    Formatting text
    When you want to print text you use the Graphics.DrawString command which has a number of overloaded implementations. The most useful of these (in my experience) is Graphics.DrawString(String, Font, Brush, RectangleF, StringFormat) which allows you to specifiy the bounding rectangle that the string will be drawn in and the StringFormat which specifies the text alignment and what to do if the text exceeds the bounding rectangle.

    So - suppose you want to draw a string that is centred in a box and wraps around on the word boundary (like a word processor does) you could specify:-
    Dim _Textlayout As New System.Drawing.StringFormat
    _Textlayout.LineAlignment = StringAlignment.Center ' center horizontally
    _Textlayout.Alignment = StringAlignment.Center ' center vertically
    _Textlayout.Trimming = StringTrimming.Word

  4. #4
    Fanatic Member
    Join Date
    Dec 2009

    Re: A beginner's guide to printing in .NET

    Hi,I have written a code for printing more than one copies of a page where the user will select the number of copies in the printdialog and on clicking ok,that copy will be printed more than once.
    Static a As Integer = 0

    Dim m As Integer = PrintDocument1.PrinterSettings.ToPage

    If a < m Then

    e.HasMorePages = True

    a += 1


    e.HasMorePages = False

    End If

    End Sub
    But this code is not working good

    please help

  5. #5

    Thread Starter
    Frenzied Member
    Join Date
    Jul 2002
    Dublin, Ireland

    Re: A beginner's guide to printing in .NET

    All of the actual printing on a page is done in the PrintPage event handler. This event is raised for each page that is printed

    So if you are initialising that static variable inside that event handler it will be reassigned each time. Declare it outside the event handler.

  6. #6
    Fanatic Member
    Join Date
    Mar 2009

    Re: A beginner's guide to printing in .NET

    how do i get a bitmap of the print preview ?
    time to amp up the beef

  7. #7
    New Member
    Join Date
    Oct 2012

    Re: A beginner's guide to printing in .NET

    Yes, but how do you insert tab stops. I have done the following and it doesn't work:
    I am trying to change the tab stops on the printer before I send formatted text (w/Tabs) to it. I can change the tab stops in stringFormat and use it when calling DrawString but they are completely ignored, e.g.,

    Dim string_format As New StringFormat
    Dim tabs As Single() = {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20}
    string_format.SetTabStops(0.0F, tabs)

    e.Graphics.DrawString(txt, m_Fonts(para.FontNumber), _
    Brushes.Black, layout_rect, string_format)

    One would 'think' that this should be pretty straight-forward.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts


Click Here to Expand Forum to Full Width

Survey posted by VBForums.