VB.Net 2008 Printing with PrintDocument

Posted on 2009-04-23
Last Modified: 2012-05-06
Hi Experts,

i need to print some information and I've begun using the printdocument, printdialog and

What i'm trying to print is contact information from my contact manager that i'm working on.

So far I have been able to get the following to print:
document header w/ company name
the billing and shipping addresses with a box around each
the data from my datagridview (date,user,note)

This is the format of page 1:
<header on page with company name>

***************  ****************
* Billing Address  *  * Shipping Address *
*                          *  *                             *
***************  ****************

<date>  <user>  <note>
<date>  <user>  <note>


<footer with page x of y>

This is the format of pages 2+

<header on page with company name>

<date>  <user>  <note>
<date>  <user>  <note>


<footer with page x of y>

what i'm having trouble with is determining when to start another page (e.hasmorepages=true) and getting it to print only the header, data from the datagridview(Contacts_NoteHistory) (date,user,note) and footer on the 2nd page.

The code below works for the header, addresses, and some of the notes, but when it reaches the bottom of the page it starts to squish the text closer together.

The code i'm using to print is below (I know - it's horrible =) )

Global_GetControlByTag is a function I wrote to get a control by its tag based on it's parent.

Private Sub PrintDocument1_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage

        ' clear the page


        ' set the fonts used

        Dim FontName As String = "Verdana"

        Dim NormalFont As New Font(FontName, 10)

        Dim BoldFont As New Font(FontName, 10, FontStyle.Bold)


        ' initializing the font to be used for printing

        Dim PrintAreaHeight, PrintAreaWidth, marginLeft, marginTop As Int32

        With PrintDocument1.DefaultPageSettings

            .Margins.Top = 50

            .Margins.Left = 50

            .Margins.Bottom = 50

            .Margins.Right = 50

            ' initializing local variables to hold margin values that will serve

            ' as the X and Y coordinates for the upper left corner of the printing

            ' area rectangle

            PrintAreaHeight = .PaperSize.Height - .Margins.Top - .Margins.Bottom

            PrintAreaWidth = .PaperSize.Width - .Margins.Left - .Margins.Right

            ' X and Y coordinate

            marginLeft = .Margins.Left

            marginTop = .Margins.Top

        End With


        If PrintDocument1.DefaultPageSettings.Landscape Then

            ' if the user selects landscape mode, swap the printing area height and width

            Dim intTemp As Int32

            intTemp = PrintAreaHeight

            PrintAreaHeight = PrintAreaWidth

            PrintAreaWidth = intTemp

        End If


        'draw temporary lines at the borders, remove when done


        e.Graphics.DrawLine(Pens.Black, marginLeft, marginTop, PrintAreaWidth, marginTop)

        e.Graphics.DrawLine(Pens.Black, PrintAreaWidth, marginTop, PrintAreaWidth, marginTop + PrintAreaHeight)

        e.Graphics.DrawLine(Pens.Black, PrintAreaWidth, marginTop + PrintAreaHeight, marginLeft, marginTop + PrintAreaHeight)

        e.Graphics.DrawLine(Pens.Black, marginLeft, marginTop, marginLeft, marginTop + PrintAreaHeight)


        ' draw the doc header

        Dim cur_text As String = Global_GetControlByTag(Contacts_AddressTabs.TabPages(0), "company").text

        Dim cur_sizeF As SizeF = e.Graphics.MeasureString(cur_text, BoldFont, PrintAreaWidth)

        e.Graphics.DrawString(cur_text, BoldFont, Brushes.Black, (PrintDocument1.DefaultPageSettings.PaperSize.Width - cur_sizeF.Width) / 2, marginTop * 1 / 3)


        Dim billing_max_len As Integer = 0

        Dim billing_num_lines As Integer = 0


        Dim max_len As Integer = 0

        Dim num_lines As Integer = 0


        Dim x As Integer = marginLeft

        Dim y As Integer = marginTop


        Dim group_spacing As Integer = 10

        Dim pad As Integer = 5


        ' draw the misc info

        cur_text = "Category: " & Contacts_ContactCategory.Text

        cur_sizeF = e.Graphics.MeasureString(cur_text, NormalFont, PrintAreaWidth)

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x, y)


        cur_text = "Found Via: " & Contacts_FoundVia.Text

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x + PrintAreaWidth / 3, y)


        cur_text = "Terms: " & Contacts_PaymentTerms.Text

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x + PrintAreaWidth * 2 / 3, y)


        cur_text = "Type: " & Contacts_ContactType.Text

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x, y + cur_sizeF.Height)


        cur_text = "Agent: " & Contacts_ContactAgent.Text

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x + PrintAreaWidth / 3, y + cur_sizeF.Height)


        cur_text = "Discount: " & Contacts_StdDiscount.Text & "%"

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x + PrintAreaWidth * 2 / 3, y + cur_sizeF.Height)


        ' draw a box around the top data

        e.Graphics.DrawRectangle(Pens.Black, x - pad, y - pad, PrintAreaWidth, 2 * cur_sizeF.Height + pad)


        y += cur_sizeF.Height * 3


        ' A list of tags for the contact fields (billing & shipping addresses)

        Dim tags As ArrayList = New ArrayList

        With tags


            .Add("address 1")

            .Add("address 2")











        End With


        ' draw the billing and shipping area

        For j As Integer = 0 To 1

            ' billing area, j=0 shipping j=1

            For i As Integer = 0 To tags.Count - 1

                If j = 0 And i = 0 Then

                    ' first round of billing

                    x = marginLeft

                ElseIf j = 1 And i = 0 Then

                    ' first round of shipping

                    billing_max_len = max_len

                    billing_num_lines = num_lines

                    num_lines = 0

                    max_len = 0

                    x = marginLeft + billing_max_len + group_spacing

                End If

                'set the current text

                Select Case i

                    Case 0, 1, 2, 9, 10, 11, 12

                        ' company,addr1,addr2,fax,cell,web,mail

                        cur_text = Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i)).text

                        If cur_text = "" Then

                            GoTo skip

                        End If

                    Case 3, 4, 5, 6

                        ' city, state, zip, country

                        If i = 3 Then

                            cur_text = Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i)).text & " " & Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i + 1)).text & ", " & Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i + 2)).text & " " & Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i + 3)).text


                            ' skip the others as they've already been done.

                            GoTo skip

                        End If

                    Case 7, 8

                        ' phone, ext

                        If i = 7 Then

                            cur_text = Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i)).text

                            If Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i + 1)).text <> "" Then

                                cur_text += " x" & Global_GetControlByTag(Contacts_AddressTabs.TabPages(j), tags.Item(i + 1)).text

                            End If


                            ' skip the others as they've already been done.

                            GoTo skip

                        End If

                End Select

                ' get the size of it

                cur_sizeF = e.Graphics.MeasureString(cur_text, NormalFont, PrintAreaWidth)

                ' update the size to hold on to the max len (for rectangle calculations)


                e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x, y + cur_sizeF.Height * num_lines)

                num_lines += 1


                If cur_sizeF.Width > max_len Then

                    max_len = cur_sizeF.Width

                End If




        ' draw the caption text and rectangles around the areas

        e.Graphics.DrawString("Billing Address", NormalFont, Brushes.Black, marginLeft, y - cur_sizeF.Height)

        e.Graphics.DrawRectangle(Pens.Black, marginLeft - pad, y, billing_max_len + 2 * pad, cur_sizeF.Height * billing_num_lines + pad)

        e.Graphics.DrawString("Shipping Address", NormalFont, Brushes.Black, marginLeft + billing_max_len + group_spacing, y - cur_sizeF.Height)

        e.Graphics.DrawRectangle(Pens.Black, marginLeft + billing_max_len - 2 * pad + 2 * group_spacing, y, max_len + 2 * pad, cur_sizeF.Height * num_lines + pad)


        ' draw the miscellaneous notes

        x = marginLeft

        y += cur_sizeF.Height * num_lines + pad


        cur_text = "Miscellanous Notes"

        cur_sizeF = e.Graphics.MeasureString(cur_text, NormalFont, PrintAreaWidth)

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x, y)


        y += cur_sizeF.Height + 5


        cur_text = Contacts_MiscNotes.Text

        cur_sizeF = e.Graphics.MeasureString(cur_text, NormalFont, PrintAreaWidth)

        e.Graphics.DrawString(cur_text, NormalFont, Brushes.Black, x, y)


        ' draw the box around miscellaneous notes

        e.Graphics.DrawRectangle(Pens.Black, x - pad, y - pad, cur_sizeF.Width + pad, cur_sizeF.Height + pad)


        ' draw the notes

        Dim datex As Integer = marginLeft

        Dim namex As Integer = marginLeft + 100

        Dim notex As Integer = namex + 50


        y += cur_sizeF.Height + 5

        Dim intLineCount As Int32 = CInt(PrintAreaHeight - y / Font.Height)


        Dim fmt As New StringFormat(StringFormatFlags.LineLimit)

        Dim intLinesFilled, intCharsFitted As Int32

        Dim intCurrentChar As Int32


        With Contacts_NoteHistory.Rows

            For i As Integer = 0 To .Count - 1

                e.Graphics.DrawString(Format(.Item(i).Cells("NoteDate").Value, "yyyy-MM-dd"), NormalFont, Brushes.Black, datex, y)

                e.Graphics.DrawString(.Item(i).Cells("UserName").Value, NormalFont, Brushes.Black, namex, y)

                cur_sizeF = e.Graphics.MeasureString(Mid(.Item(i).Cells("NoteText").Value, intCurrentChar + 1), NormalFont, New SizeF(PrintAreaWidth - notex - pad, PrintAreaHeight - y), fmt, intCharsFitted, intLinesFilled)

                Dim rectPrintingArea As New RectangleF(notex, y, PrintAreaWidth - notex - pad, PrintAreaHeight - y)

                e.Graphics.DrawString(Mid(.Item(i).Cells("NoteText").Value, intCurrentChar + 1), NormalFont, Brushes.Black, rectPrintingArea, fmt)


        End With

    End Sub

Open in new window

Question by:sgaggerj
    LVL 14

    Accepted Solution

    Here are my thoughts.   Maybe you could create a separate method for both the header and footer printing and the page content.   Then on the print page, keep track of the page you are on and call the correct methods.  maybe use a global variable to track the page number.  Here is some psuedo code.

    Print_Page Method
    SELECT CASE on Page number
       Page 1
      Page 2

    Someplace in here, determien if you have another page to print.. If so,
         Set pagenumber++
         e.HasMorePages = true
    End Print_Page Method
    LVL 1

    Author Comment

    That's the angle i was going for, but i'm having trouble 1) figuring out when to start the 2nd page and 2) clearing the 2nd page - i tried but it didn't clear the printdocument for the 2nd page...

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    Highfive + Dolby Voice = No More Audio Complaints!

    Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

    Article by: jpaulino
    XML Literals are a great way to handle XML files and the community doesn’t use it as much as it should.  An XML Literal is like a String ( Literal, only instead of starting and ending with w…
    1.0 - Introduction Converting Visual Basic 6.0 (VB6) to Visual Basic 2008+ (VB.NET). If ever there was a subject full of murkiness and bad decisions, it is this one!   The first problem seems to be that people considering this task of converting…
    Sending a Secure fax is easy with eFax Corporate ( First, Just open a new email message.  In the To field, type your recipient's fax number You can even send a secure international fax — just include t…
    This video discusses moving either the default database or any database to a new volume.

    761 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    12 Experts available now in Live!

    Get 1:1 Help Now