0

I am trying to batch print data from DataGridView using vb.net, a row per page. The current code just loops. I would like to process and print all rows and get to ask only once where to save the PDF. By the way, I have set the printer to PDF printer. The output should be 6 pages.

This is my code so far.

Imports System.Drawing.Printing
Imports System.Text.RegularExpressions

Public Class Form1
    Public currentRow As Integer
    Public totPages As Integer
    Public pageNumber As Integer
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        For i As Integer = 0 To 5
            DataGridView1.Rows.Add("Yes", "someData" & i, "someData" & i, "someData" & i, "someData" & i)
        Next


    End Sub
    Private Sub printButton_Click(sender As Object, e As EventArgs) Handles printButton.Click
        Dim pdS As New PrintDocument()

        pdS.DefaultPageSettings.Landscape = False

        Dim PrintDialog1 As New PrintDialog
        PrintDialog1.Document = pdS
        totPages = 0
        pageNumber = 0
        For Each row As DataGridViewRow In DataGridView1.Rows
            If row.Cells(0).Value = "Yes" Then
                totPages = totPages + 1
            End If
        Next

        AddHandler pdS.PrintPage, AddressOf pds_PrintPage

        If (PrintDialog1.ShowDialog = DialogResult.OK) Then
            For Each row As DataGridViewRow In DataGridView1.Rows
                If row.Cells(0).Value = "Yes" Then
                    currentRow = row.Index
                    pageNumber = pageNumber + 1
                    pdS.Print()
                End If
            Next
        End If

    End Sub

    Private Sub pds_PrintPage(sender As Object, ev As PrintPageEventArgs)

        'Drawing
        Dim inputData As String
        Dim calReg As New Font("Calibri", 8, FontStyle.Regular)
        With DataGridView1
            inputData = .Rows(currentRow).Cells(1).Value & vbCrLf & .Rows(currentRow).Cells(2).Value & vbCrLf &
                .Rows(currentRow).Cells(3).Value & vbCrLf & .Rows(currentRow).Cells(4).Value
        End With

        Dim nextY As Double
        nextY = ev.MarginBounds.Y
        ev.Graphics.DrawString(inputData, calReg, Brushes.Black, ev.MarginBounds.X, nextY)

        If totPages > pageNumber Then
            ev.HasMorePages = True
            Exit Sub
        Else
            ev.HasMorePages = False
        End If

    End Sub
End Class


  • 1
    If you want to print multiple pages then you must set `e.HasMorePages` to `True`. You don't attach the event handler multiple times. Read about `HasMorePages`, do your best to use it and, if that doesn't work, post here and show us what you did. As it stands, if you haven't used `HasMorePages` then you haven't tried to print more pages so there's no actual problem for us to fix at this sage. – jmcilhinney Jul 15 '20 at 08:54
  • I have updated the code above with `ev.HasMorePages` but it doesn't work. – MyXbox Account Jul 15 '20 at 09:15
  • This is a classic case of writing code without knowing what it's got to do first. You're still attaching the event handler multiple times, which I specifically said not to do. None of `totPages`, `pageNumber` or `currentRow` change value between executions of that event handler so how could you expect it to do anything different each time? Of course it only prints the last row because you set `currentRow` to that index and print that row every time. Start by working out what you have to do. Until you've done that, don;t write any code. Work your algorithm manually first. – jmcilhinney Jul 15 '20 at 09:24
  • My bad, I have updated the code. But `hasmorepages` doesn't seem to do anything. – MyXbox Account Jul 15 '20 at 09:32
  • That code is a complete disaster. Get rid of all of it and start again. You don't call `Print` multiple times. You call `Print` once and it will raise the `PrintPage` event. After printing a page, you determine whether there are more pages to print and set `HasMorePages` accordingly. How can your condition for checking possibly be useful when neither value that you compare ever changes? Put some though into what the code needs to do first. Do that manually yourself. Don't write any code until you can manually run an algorithm. – jmcilhinney Jul 15 '20 at 10:22
  • Here's an idea. How about you get the rows from the grid that need to be printed and put them into a list. You can then print a page for a row and remove it from the list. `HasMorePages` will be `True` as long as there are items in that list. Simple, huh? That's got nothing to do with code. It's just logic, which is why you should be working out the logic before writing code. – jmcilhinney Jul 15 '20 at 10:23

1 Answers1

1

You can create a Queue of the target DataGridViewRow objects, dequeue a row in the PrintPage handler to print it, and set the HasMorePages property to True if the queue has more rows.

The method below is to print or preview.

Private Sub Print(Optional IsPreview As Boolean = False)
    Using pdoc As New PrintDocument,
            pd As New PrintDialog With {.Document = pdoc},
            pp As New PrintPreviewDialog With {.Document = pdoc}

        pdoc.DefaultPageSettings.Landscape = False

        If Not IsPreview AndAlso pd.ShowDialog <> DialogResult.OK Then Return

        Dim q As New Queue(Of DataGridViewRow)

        DataGridView1.Rows.Cast(Of DataGridViewRow).
            Where(Function(r) Not r.IsNewRow AndAlso r.Cells(0).Value.ToString = "Yes").
            ToList.ForEach(Sub(r) q.Enqueue(r))

        AddHandler pdoc.PrintPage,
            Sub(s, e)
                Using f As New Font("Calibri", 8, FontStyle.Regular)
                    Dim r = q.Dequeue
                    Dim x = e.MarginBounds.X
                    Dim y = e.MarginBounds.Y

                    For Each cell In r.Cells.Cast(Of DataGridViewCell).Skip(1)
                        e.Graphics.DrawString(cell.Value.ToString, f, Brushes.Black, x, y)
                        y += f.Height
                    Next

                    e.HasMorePages = q.Count > 0
                End Using
            End Sub

        If IsPreview Then
            pp.ShowDialog()
        Else
            pdoc.Print()
        End If
    End Using
End Sub

This will printout 6 pages for the given example. Call the method as follows:

Private Sub printButton_Click(sender As Object, e As EventArgs) Handles printButton.Click
    Print() 'To print...
    Print(True) 'To preview...
End Sub