Home All Groups Group Topic Archive Search About

PrintDocument and HasMorePages issue

Author
21 Sep 2006 2:59 PM
kig25
Hello,

When using the VB.NET PrintDocument class, I seem to be encountering an
issue where the sub pd_PrintPage handles PrintDocument.PrintPage (upon
continuing if HasMorePages = true) will paint the next page on top of
the original page.  In a sample data case where the source is long
enough to fill three pages, I end up with four calls to pd_PrintPage
which render onto two printed sheets.  Historical posts in this forum
from 2003 and 2005 suggested several tactics:

1) Explicitly call Exit Sub after setting HasMorePages = true
2) Defining the PrintDocument object in code instead of with the visual
component
3) Avoiding the Addhandler statement before calling pd.Print():
AddHandler pd.PrintPage, AddressOf Me.pd_PrintPage

In my current code, I have implemented # 1 and 2.  I was unable to get
any output under #3.  Are there any further tactics to try?

I remain suspicious of the AddHandler, though I haven't been able to
work around it yet -- could I have two concurrent pd_PrintPage threads,
rendering to the same target?  It seems unlikely, because my page
counter is incremented between these calls.

I thought I had this resolved yesterday with this code as-is, but as it
happens, it worked successfully to print 3 distinct pages under only
one case: where I had put in debugging break at the start of
pd_PrintPage and stepped through all steps.

Even if I put in a few breaks, but "F5" to accelerate between them, the
issue continues to occur.  Nevertheless, I've been trying to identify
the specific step (maybe I should introduce an artifical delay?  In the
print event model, is there a way to force the pd_PrintPage execution
to wait for, ie, synchronous feedback from the printer before
continuing to render the next page if I suspect the local network to
the printer is slow?)

I once suspected I was sloppy about margins (and have attempted in code
to be somewhat cleaner), and that I may have pixels rendering outside
the printable bounds of the page, but I don't think this would cause
the PrintPage re-iteration to stay on the same sheet.  Also, because
the app worked on debug mode with the same data and graphic element
positioning, I no longer suspect this angle.  Is this something to
pursue?

Here is the code I'm working with.  There is a button click event
handler, a beginPrint which initializes PrintPageCount as a page
counter, and the PrintPage sub, which uses the page counter to
determine which elements from an array of my own user controls
(ComponentCollection) to render on the page.  This also drives my
HasMorePages = T/F logic.  PrintALabel and PrintATextbox are overloaded
methods I wrote that apply the offset parameter to the object's
location, and call appropriate graphics.DrawRectangle and
graphics.DrawString methods.

'****************************************************************
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
        Try
            'Assumes the default printer.
            'Dim pd As System.Drawing.Printing.PrintDocument =
PrintDocument1
            pd = New System.Drawing.Printing.PrintDocument()

            Dim margins As New Printing.Margins(25, 25, 25, 25)
            pd.DefaultPageSettings.Margins = margins

            AddHandler pd.PrintPage, AddressOf Me.pd_PrintPage
            ' pd.DefaultPageSettings.Landscape = True
            pd.Print()
        Catch ex As Exception
            MessageBox.Show(ex.Message, "An error occurred while
printing")
        End Try
    End Sub

'***************************************************************

    Private Sub pd_BeginPrint(ByVal sender As Object, ByVal e As
System.Drawing.Printing.PrintEventArgs) Handles pd.BeginPrint
        PrintPageCount = 0
        FormLocation = Me.ComponentCollection(0).Location
        FormLocation.Y = FormLocation.Y -
PrintDocument1.DefaultPageSettings.HardMarginY '- 20
        FormLocation.X = PrintDocument1.DefaultPageSettings.HardMarginX
        'Me.ScrollControlIntoView(Me.ComponentCollection(0))
    End Sub

'***************************************************************

Private Sub pd_PrintPage(ByVal sender As Object, ByVal ev As
System.Drawing.Printing.PrintPageEventArgs) Handles pd.PrintPage
        ' Draw a picture.
        Dim thisFont As New System.Drawing.Font("Microsoft Sans Serif",
8.25!, CType((System.Drawing.FontStyle.Regular),
System.Drawing.FontStyle), System.Drawing.GraphicsUnit.Point, CType(0,
Byte))
        Dim thisFormat As New System.Drawing.StringFormat()

        'In future, calculate components per page based on size and
paper setting.
        Dim ComponentsPerPage As Single = 11

        Dim indexStart As Integer = PrintPageCount * ComponentsPerPage
        Dim indexEnd As Integer
        Dim LastPage As Boolean = False

        If Me.ComponentCollection.Length - PrintPageCount *
ComponentsPerPage <= ComponentsPerPage Then
            ' last page to print
            indexEnd = indexStart + Me.ComponentCollection.Length -
PrintPageCount * ComponentsPerPage - 1
            LastPage = True
        Else
            indexEnd = (PrintPageCount + 1) * ComponentsPerPage - 1
'indexStart +
        End If
        ''
Me.ScrollControlIntoView(Me.ComponentCollection(indexStart))

        Dim item As SingleComponentControl
        Dim i As Integer
        ' For Each item In Me.ComponentCollection
        For i = indexStart To indexEnd
            item = Me.ComponentCollection(i)

            Dim rePoint As New System.Drawing.Point(FormLocation.X,
item.Location.Y - PrintPageCount * ComponentsPerPage * (item.Height +
5) - FormLocation.Y)
            '  +5 to height is spacing between components.
            item.PrinterCoord = rePoint

            ev.Graphics.DrawRectangle(Pens.Gray, item.PrinterCoord.X,
item.PrinterCoord.Y, item.Width - 80, item.Height)
            PrintATextbox(item.txtConsumed, item, ev,
Drawing2D.HatchStyle.Percent50)
            PrintATextbox(item.txtOnHand, item, ev,
Drawing2D.HatchStyle.Percent05)
            PrintATextbox(item.TxtOutstanding, item, ev,
Drawing2D.HatchStyle.Percent75)
            Dim lblpt As New Point(5, 36)
            PrintALabel(item.Label1, item, ev, lblpt)
            Dim shiftleft As New Point(-50, 0)
            PrintALabel(item.Label2, item, ev, shiftleft)
' ... etc.  Several more labels removed for clarity
            PrintALabel(item.Label11, item, ev, shiftleft)
        Next i

        ' iterate page count, footer, and test

        PrintPageCount = PrintPageCount + 1

'footer with current page number
        ev.Graphics.DrawString("Inventory/Component Report for Job " +
JobNumber.ToString + " -- Page " + PrintPageCount.ToString, thisFont,
Brushes.Black, 40, 950)
        ' ev.HasMorePages = False

        If Not LastPage Then
            ev.HasMorePages = True
            Exit Sub
        Else
            ev.HasMorePages = False
        End If
    End Sub

'***********************************************************
Thank you to anyone for any insight into this issue.

Regards,
Keith Gerritsen

Author
21 Sep 2006 4:53 PM
kgerritsen
I have a solution, but I'm not jazzed by it.  I moved the page-count
initialization to the button event, and wrapped a Where around the
pd.Print():


            PrintPageCount = 0
            Dim LastPage As Boolean = False

            While Not LastPage
                'try print as separate docs...
                'pd.Print()
                If Me.ComponentCollection.Length - PrintPageCount *
ComponentsPerPage <= ComponentsPerPage Then
                    ' last page to print
                    LastPage = True
                Else
                    LastPage = False
                End If
                pd.Print()
                PrintPageCount = PrintPageCount + 1
            End While

It still calls the PrintPage twice for each page, but as I don't
increment PrintPageCount in this procedure any more, rendering twice on
each output is of no consequence.

I am managing my page counts and printing each page as a separate
1-page document now.  It works, and I'm done with this, but I don't
exactly like it.  If I was going to implement a "cancel print" feature,
it would be a pain to accomodate.

Regards,
Keith


ki***@drexel.edu wrote:
Show quoteHide quote
> Hello,
>
> When using the VB.NET PrintDocument class, I seem to be encountering an
> issue where the sub pd_PrintPage handles PrintDocument.PrintPage (upon
> continuing if HasMorePages = true) will paint the next page on top of
> the original page.  In a sample data case where the source is long
> enough to fill three pages, I end up with four calls to pd_PrintPage
> which render onto two printed sheets.  Historical posts in this forum
> from 2003 and 2005 suggested several tactics:
>
> 1) Explicitly call Exit Sub after setting HasMorePages = true
> 2) Defining the PrintDocument object in code instead of with the visual
> component
> 3) Avoiding the Addhandler statement before calling pd.Print():
> AddHandler pd.PrintPage, AddressOf Me.pd_PrintPage
>
> In my current code, I have implemented # 1 and 2.  I was unable to get
> any output under #3.  Are there any further tactics to try?
>
> I remain suspicious of the AddHandler, though I haven't been able to
> work around it yet -- could I have two concurrent pd_PrintPage threads,
> rendering to the same target?  It seems unlikely, because my page
> counter is incremented between these calls.
>
> I thought I had this resolved yesterday with this code as-is, but as it
> happens, it worked successfully to print 3 distinct pages under only
> one case: where I had put in debugging break at the start of
> pd_PrintPage and stepped through all steps.
>
> Even if I put in a few breaks, but "F5" to accelerate between them, the
> issue continues to occur.  Nevertheless, I've been trying to identify
> the specific step (maybe I should introduce an artifical delay?  In the
> print event model, is there a way to force the pd_PrintPage execution
> to wait for, ie, synchronous feedback from the printer before
> continuing to render the next page if I suspect the local network to
> the printer is slow?)
>
> I once suspected I was sloppy about margins (and have attempted in code
> to be somewhat cleaner), and that I may have pixels rendering outside
> the printable bounds of the page, but I don't think this would cause
> the PrintPage re-iteration to stay on the same sheet.  Also, because
> the app worked on debug mode with the same data and graphic element
> positioning, I no longer suspect this angle.  Is this something to
> pursue?
>
> Here is the code I'm working with.  There is a button click event
> handler, a beginPrint which initializes PrintPageCount as a page
> counter, and the PrintPage sub, which uses the page counter to
> determine which elements from an array of my own user controls
> (ComponentCollection) to render on the page.  This also drives my
> HasMorePages = T/F logic.  PrintALabel and PrintATextbox are overloaded
> methods I wrote that apply the offset parameter to the object's
> location, and call appropriate graphics.DrawRectangle and
> graphics.DrawString methods.
>
> '****************************************************************
>     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
> System.EventArgs) Handles Button1.Click
>         Try
>             'Assumes the default printer.
>             'Dim pd As System.Drawing.Printing.PrintDocument =
> PrintDocument1
>             pd = New System.Drawing.Printing.PrintDocument()
>
>             Dim margins As New Printing.Margins(25, 25, 25, 25)
>             pd.DefaultPageSettings.Margins = margins
>
>             AddHandler pd.PrintPage, AddressOf Me.pd_PrintPage
>             ' pd.DefaultPageSettings.Landscape = True
>             pd.Print()
>         Catch ex As Exception
>             MessageBox.Show(ex.Message, "An error occurred while
> printing")
>         End Try
>     End Sub
>
> '***************************************************************
>
>     Private Sub pd_BeginPrint(ByVal sender As Object, ByVal e As
> System.Drawing.Printing.PrintEventArgs) Handles pd.BeginPrint
>         PrintPageCount = 0
>         FormLocation = Me.ComponentCollection(0).Location
>         FormLocation.Y = FormLocation.Y -
> PrintDocument1.DefaultPageSettings.HardMarginY '- 20
>         FormLocation.X = PrintDocument1.DefaultPageSettings.HardMarginX
>         'Me.ScrollControlIntoView(Me.ComponentCollection(0))
>     End Sub
>
> '***************************************************************
>
>  Private Sub pd_PrintPage(ByVal sender As Object, ByVal ev As
> System.Drawing.Printing.PrintPageEventArgs) Handles pd.PrintPage
>         ' Draw a picture.
>         Dim thisFont As New System.Drawing.Font("Microsoft Sans Serif",
> 8.25!, CType((System.Drawing.FontStyle.Regular),
> System.Drawing.FontStyle), System.Drawing.GraphicsUnit.Point, CType(0,
> Byte))
>         Dim thisFormat As New System.Drawing.StringFormat()
>
>         'In future, calculate components per page based on size and
> paper setting.
>         Dim ComponentsPerPage As Single = 11
>
>         Dim indexStart As Integer = PrintPageCount * ComponentsPerPage
>         Dim indexEnd As Integer
>         Dim LastPage As Boolean = False
>
>         If Me.ComponentCollection.Length - PrintPageCount *
> ComponentsPerPage <= ComponentsPerPage Then
>             ' last page to print
>             indexEnd = indexStart + Me.ComponentCollection.Length -
> PrintPageCount * ComponentsPerPage - 1
>             LastPage = True
>         Else
>             indexEnd = (PrintPageCount + 1) * ComponentsPerPage - 1
> 'indexStart +
>         End If
>         ''
> Me.ScrollControlIntoView(Me.ComponentCollection(indexStart))
>
>         Dim item As SingleComponentControl
>         Dim i As Integer
>         ' For Each item In Me.ComponentCollection
>         For i = indexStart To indexEnd
>             item = Me.ComponentCollection(i)
>
>             Dim rePoint As New System.Drawing.Point(FormLocation.X,
> item.Location.Y - PrintPageCount * ComponentsPerPage * (item.Height +
> 5) - FormLocation.Y)
>             '  +5 to height is spacing between components.
>             item.PrinterCoord = rePoint
>
>             ev.Graphics.DrawRectangle(Pens.Gray, item.PrinterCoord.X,
> item.PrinterCoord.Y, item.Width - 80, item.Height)
>             PrintATextbox(item.txtConsumed, item, ev,
> Drawing2D.HatchStyle.Percent50)
>             PrintATextbox(item.txtOnHand, item, ev,
> Drawing2D.HatchStyle.Percent05)
>             PrintATextbox(item.TxtOutstanding, item, ev,
> Drawing2D.HatchStyle.Percent75)
>             Dim lblpt As New Point(5, 36)
>             PrintALabel(item.Label1, item, ev, lblpt)
>             Dim shiftleft As New Point(-50, 0)
>             PrintALabel(item.Label2, item, ev, shiftleft)
> ' ... etc.  Several more labels removed for clarity
>             PrintALabel(item.Label11, item, ev, shiftleft)
>         Next i
>
>         ' iterate page count, footer, and test
>
>         PrintPageCount = PrintPageCount + 1
>
> 'footer with current page number
>         ev.Graphics.DrawString("Inventory/Component Report for Job " +
> JobNumber.ToString + " -- Page " + PrintPageCount.ToString, thisFont,
> Brushes.Black, 40, 950)
>         ' ev.HasMorePages = False
>
>         If Not LastPage Then
>             ev.HasMorePages = True
>             Exit Sub
>         Else
>             ev.HasMorePages = False
>         End If
>     End Sub
>
> '***********************************************************
> Thank you to anyone for any insight into this issue.
>
> Regards,
> Keith Gerritsen