Search code examples
vb.netprint-preview

What's wrong with this code? it's not showing the first row in PrintPreview


the code below was actually copied from one of the stackoverflow answers, it's not showing the first row in print preview. Its skipping the first row and shwing starting from second.

here's the code,

Dim mRow As Integer = 0
Dim newpage As Boolean = True
Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
    newpage = True
    With dgvData
        Dim fmt As StringFormat = New StringFormat(StringFormatFlags.LineLimit)
        'Dim fmt2 As StringFormat = New StringFormat(StringFormatFlags.LineLimit)
        fmt.LineAlignment = StringAlignment.Center
        'Dim font As Font

        'font = New Font("Microsoft Sans Serif", "13", FontStyle.Underline, GraphicsUnit.Inch, gdiCharSet:=1)
        'fmt.Trimming = StringTrimming.EllipsisCharacter
        Dim rc1 As RectangleF = New RectangleF(460, 20, 350, 20)
        Dim rc2 As RectangleF = New RectangleF(500, 40, 350, 20)

        e.Graphics.DrawString("Some Heading", .Font, Brushes.Black, rc1, fmt)
        e.Graphics.DrawString("Another heading ", .Font, Brushes.Black, rc2, fmt)

        Dim y As Single = e.MarginBounds.Top + 60
        Do While mRow < .RowCount
            Dim row As DataGridViewRow = .Rows(mRow)
            Dim x As Single = e.MarginBounds.Left
            Dim h As Single = 0
            For Each cell As DataGridViewCell In row.Cells
                Dim rc As RectangleF = New RectangleF(x, y, cell.Size.Width, cell.Size.Height)
                e.Graphics.DrawRectangle(Pens.Black, rc.Left, rc.Top, rc.Width, rc.Height)
                If (newpage) Then
                    e.Graphics.DrawString(dgvData.Columns(cell.ColumnIndex).HeaderText.ToString, .Font, Brushes.Black, rc, fmt)
                    'MessageBox.Show(dgvData.Columns(cell.ColumnIndex).HeaderText.ToString)
                Else
                    e.Graphics.DrawString(dgvData.Rows(cell.RowIndex).Cells(cell.ColumnIndex).FormattedValue.ToString(), .Font, Brushes.Black, rc, fmt)
                    'MessageBox.Show(dgvData.Rows(cell.RowIndex).ToString)
                End If
                x += rc.Width
                h = Math.Max(h, rc.Height)
            Next
            newpage = False
            y += h
            mRow += 1
            If y + h > e.MarginBounds.Bottom Then
                e.HasMorePages = True
                mRow -= 1
                newpage = True
                Exit Sub
            End If
        Loop
        mRow = 0
    End With
End Sub

Solution

  • [If I'm understanding this correctly,] It skips the first line because newpage is set to True, so when you get to this code:

    If (newpage) Then
        e.Graphics.DrawString(dgvData.Columns(cell.ColumnIndex).HeaderText.ToString, .Font, Brushes.Black, rc, fmt)
        'MessageBox.Show(dgvData.Columns(cell.ColumnIndex).HeaderText.ToString)
    Else
        e.Graphics.DrawString(dgvData.Rows(cell.RowIndex).Cells(cell.ColumnIndex).FormattedValue.ToString(), .Font, Brushes.Black, rc, fmt)
        'MessageBox.Show(dgvData.Rows(cell.RowIndex).ToString)
    End If
    

    you are printing a header, instead of the cell in the current line (Dim row As DataGridViewRow = .Rows(mRow)).

    Edit in response to "So should I set the newage = false at start, or is there anything else I should do?"

    If newpage evaluates to True, you need to output BOTH the header and the current line. In the original code you are using an If switch to do one or the other (thus skipping output of the current line). I did some quick reorganizing, and below is my new suggested code in its entirety, so you can compare.

    Dim mRow As Integer = 0
    Dim newpage As Boolean = True
    Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage
        newpage = True
        With dgvData
            Dim fmt As StringFormat = New StringFormat(StringFormatFlags.LineLimit)
            'Dim fmt2 As StringFormat = New StringFormat(StringFormatFlags.LineLimit)
            fmt.LineAlignment = StringAlignment.Center
            'Dim font As Font
    
            'font = New Font("Microsoft Sans Serif", "13", FontStyle.Underline, GraphicsUnit.Inch, gdiCharSet:=1)
            'fmt.Trimming = StringTrimming.EllipsisCharacter
            Dim rc1 As RectangleF = New RectangleF(460, 20, 350, 20)
            Dim rc2 As RectangleF = New RectangleF(500, 40, 350, 20)
    
            e.Graphics.DrawString("Some Heading", .Font, Brushes.Black, rc1, fmt)
            e.Graphics.DrawString("Another heading ", .Font, Brushes.Black, rc2, fmt)
    
            Dim rc As RectangleF
            Dim y As Single = e.MarginBounds.Top + 60
            Do While mRow < .RowCount
                Dim row As DataGridViewRow = .Rows(mRow)
                Dim x As Single = e.MarginBounds.Left
                Dim h As Single = 0
                If (newpage) Then
                    For Each cell As DataGridViewCell In row.Cells
                        rc = New RectangleF(x, y, cell.Size.Width, cell.Size.Height)
                        e.Graphics.DrawRectangle(Pens.Black, rc.Left, rc.Top, rc.Width, rc.Height)
                        e.Graphics.DrawString(dgvData.Columns(cell.ColumnIndex).HeaderText.ToString, .Font, Brushes.Black, rc, fmt)
                        'MessageBox.Show(dgvData.Columns(cell.ColumnIndex).HeaderText.ToString)
                        x += rc.Width
                        h = Math.Max(h, rc.Height)
                    Next
                    newpage = False
                    y += h
                    x = e.MarginBounds.Left
                    h = 0
                End If
                For Each cell As DataGridViewCell In row.Cells
                    rc = New RectangleF(x, y, cell.Size.Width, cell.Size.Height)
                    e.Graphics.DrawRectangle(Pens.Black, rc.Left, rc.Top, rc.Width, rc.Height)
                    e.Graphics.DrawString(dgvData.Rows(cell.RowIndex).Cells(cell.ColumnIndex).FormattedValue.ToString(), .Font, Brushes.Black, rc, fmt)
                    'MessageBox.Show(dgvData.Rows(cell.RowIndex).ToString)
                    x += rc.Width
                    h = Math.Max(h, rc.Height)
                Next
                y += h
                mRow += 1
                If y + h > e.MarginBounds.Bottom Then
                    e.HasMorePages = True
                    mRow -= 1
                    newpage = True
                    Exit Sub
                End If
            Loop
            mRow = 0
        End With
    End Sub
    

    Some of the changes to note:

    • The newpage switch is segregated from the For Each that outputs the actual cells. This causes the current row to be output regardless of the newpage condition.
    • Some variables (x, h, and y) have to be incremented or modified both inside the switch as well as outside, so those lines are now duplicated.
    • The declaration of rc was moved outside the Do While loop. There's no need to declare it for every cell; instead just assign a new value.
    • There's really no need to check y + h > e.MarginBounds.Bottom more than once (unless you're printing REALLY BIG), as you can assume that you're only ever printing more than one line if you're already at the top of a new page, so it stays by itself at the end of the loop.
    • When you evaluate Math.Max(h, rc.Height), h is always zero or equal to rc.Height, so you might consider rethinking this.