How to print datagridview table with its header in vb.net?
Solution 1:
The original code is a nice start but has a couple of bugs and inefficiecies:
- It uses the
newpage
flag to print the header or the first row when there is a new page. Obviously you really want it to do both - Printing the column headers is done once per page, so it doesnt need to be in the data print loop at all
- It is not allowing for invisible columns or columns with other than default alignment, There could be other such settings you want to account for.
- Because it is not actually printing the correct number of rows, once you fix that you'll find that it reprints the last row from the previous page as the first row of a new page.
- There is an internal gutter or margin so that text does not print too close to gridlines - this just uses an offset of 1 or 2
- It is also needlessly using
single
andRectangleF
- It is also not prepared for the Document to be shown again or Printed. You will also want to reset
mRow
andnewpage
either in the button click orBeginPrint
event.
I added a few comments as well as coloring the header row and demonstrating how to implement things like a RowPrePaint
rule.
Private mRow As Integer = 0
Private newpage As Boolean = True
Private Sub PrintDocument1_PrintPage(sender As System.Object,
e As PrintPageEventArgs) Handles PrintDocument1.PrintPage
' sets it to show '...' for long text
Dim fmt As StringFormat = New StringFormat(StringFormatFlags.LineLimit)
fmt.LineAlignment = StringAlignment.Center
fmt.Trimming = StringTrimming.EllipsisCharacter
Dim y As Int32 = e.MarginBounds.Top
Dim rc As Rectangle
Dim x As Int32
Dim h As Int32 = 0
Dim row As DataGridViewRow
' print the header text for a new page
' use a grey bg just like the control
If newpage Then
row = dgvZZ.Rows(mRow)
x = e.MarginBounds.Left
For Each cell As DataGridViewCell In row.Cells
' since we are printing the control's view,
' skip invidible columns
If cell.Visible Then
rc = New Rectangle(x, y, cell.Size.Width, cell.Size.Height)
e.Graphics.FillRectangle(Brushes.LightGray, rc)
e.Graphics.DrawRectangle(Pens.Black, rc)
' reused in the data pront - should be a function
Select Case dgvZZ.Columns(cell.ColumnIndex).DefaultCellStyle.Alignment
Case DataGridViewContentAlignment.BottomRight,
DataGridViewContentAlignment.MiddleRight
fmt.Alignment = StringAlignment.Far
rc.Offset(-1, 0)
Case DataGridViewContentAlignment.BottomCenter,
DataGridViewContentAlignment.MiddleCenter
fmt.Alignment = StringAlignment.Center
Case Else
fmt.Alignment = StringAlignment.Near
rc.Offset(2, 0)
End Select
e.Graphics.DrawString(dgvZZ.Columns(cell.ColumnIndex).HeaderText,
dgvZZ.Font, Brushes.Black, rc, fmt)
x += rc.Width
h = Math.Max(h, rc.Height)
End If
Next
y += h
End If
newpage = False
' now print the data for each row
Dim thisNDX As Int32
For thisNDX = mRow To dgvZZ.RowCount - 1
' no need to try to print the new row
If dgvZZ.Rows(thisNDX).IsNewRow Then Exit For
row = dgvZZ.Rows(thisNDX)
x = e.MarginBounds.Left
h = 0
' reset X for data
x = e.MarginBounds.Left
' print the data
For Each cell As DataGridViewCell In row.Cells
If cell.Visible Then
rc = New Rectangle(x, y, cell.Size.Width, cell.Size.Height)
' SAMPLE CODE: How To
' up a RowPrePaint rule
'If Convert.ToDecimal(row.Cells(5).Value) < 9.99 Then
' Using br As New SolidBrush(Color.MistyRose)
' e.Graphics.FillRectangle(br, rc)
' End Using
'End If
e.Graphics.DrawRectangle(Pens.Black, rc)
Select Case dgvZZ.Columns(cell.ColumnIndex).DefaultCellStyle.Alignment
Case DataGridViewContentAlignment.BottomRight,
DataGridViewContentAlignment.MiddleRight
fmt.Alignment = StringAlignment.Far
rc.Offset(-1, 0)
Case DataGridViewContentAlignment.BottomCenter,
DataGridViewContentAlignment.MiddleCenter
fmt.Alignment = StringAlignment.Center
Case Else
fmt.Alignment = StringAlignment.Near
rc.Offset(2, 0)
End Select
e.Graphics.DrawString(cell.FormattedValue.ToString(),
dgvZZ.Font, Brushes.Black, rc, fmt)
x += rc.Width
h = Math.Max(h, rc.Height)
End If
Next
y += h
' next row to print
mRow = thisNDX + 1
If y + h > e.MarginBounds.Bottom Then
e.HasMorePages = True
' mRow -= 1 causes last row to rePrint on next page
newpage = True
Return
End If
Next
End Sub
Note that there is an Id
column set to invisible in the DGV, the Color
column is centered and Price
is left justified - these are all settings picked up from the control. Also note that the text is moved away from the gridlines just a bit.
The last bullet point above, You will also want to reset mRow
and newpage
either in the button click or BeginPrint
event. means this:
Private Sub PrintDocument1_BeginPrint(sender As Object,
e As PrintEventArgs) Handles PrintDocument1.BeginPrint
mRow = 0
newpage = True
PrintPreviewDialog1.PrintPreviewControl.StartPage = 0
PrintPreviewDialog1.PrintPreviewControl.Zoom = 1.0
End Sub
After you preview the mRow
variable will indicate that all the rows have been printed. If the user clicks Print or goes back for another Preview, nothing will print. This code also resets the first page to show and the initial Zoom.