The example shows how to print text and a multiple page DataGridView using a PrintDocument Component and GDI+.
It also shows how to preview your printing in a System.Windows.Forms .PrintPreviewDialog.
This is designed and written dynamically, i.e. it's not a one time code, it's versatile and can be used for any multi (printed) page DataGridView. Try resizing the Columns or Rows and you'll see what I mean.
The majority of the code in this example is for calculating printed page ranges for printing the DataGridView. The actual printing is very simple, as it uses standard GDI+ techniques in the PrintDocument1_PrintPage event (System.Drawing.Printing.PrintDocument).
The Graphics Device Interface (GDI) is a Microsoft Windows application programming interface and core operating system component responsible for representing graphical objects and transmitting them to output devices such as monitors and printers.
GDI is responsible for tasks such as drawing lines and curves, rendering fonts and handling palettes.
The source code for this example contains two notable methods... the PrintDocument_BeginPrint event and the PrintDocument_PrintPage event.
The ranges to be printed are measured and recorded in the PrintDocument_BeginPrint event, and the data recorded is then used in the PrintDocument_PrintPage event where the Label and DGV to be printed is rendered onto your printed page.
''' <summary> ''' the majority of this Sub is calculating printed page ranges ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub PrintDocument1_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs) Handles PrintDocument1.BeginPrint ''this removes the printed page margins PrintDocument1.OriginAtMargins = True PrintDocument1.DefaultPageSettings.Margins = New Drawing.Printing.Margins(0, 0, 0, 0) pages = New Dictionary(Of Integer, pageDetails) Dim maxWidth As Integer Dim maxHeight As Integer If PrintDocument1.DefaultPageSettings.Landscape = False Then maxWidth = CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Width) - 120 maxHeight = CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Height) - 120 + Label1.Height Else maxWidth = CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Height) - 40 maxHeight = CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Width) - 150 + Label1.Height End If Dim pageCounter As Integer = 0 pages.Add(pageCounter, New pageDetails) Dim columnCounter As Integer = 0 Dim columnSum As Integer = DataGridView1.RowHeadersWidth For c As Integer = 0 To DataGridView1.Columns.Count - 1 If columnSum + DataGridView1.Columns(c).Width < maxWidth Then columnSum += DataGridView1.Columns(c).Width columnCounter += 1 Else pages(pageCounter) = New pageDetails With {.columns = columnCounter, .rows = 0, .startCol = pages(pageCounter).startCol} columnSum = DataGridView1.RowHeadersWidth + DataGridView1.Columns(c).Width columnCounter = 1 pageCounter += 1 pages.Add(pageCounter, New pageDetails With {.startCol = c}) End If If c = DataGridView1.Columns.Count - 1 Then If pages(pageCounter).columns = 0 Then pages(pageCounter) = New pageDetails With {.columns = columnCounter, .rows = 0, .startCol = pages(pageCounter).startCol} End If End If Next maxPagesWide = pages.Keys.Max + 1 pageCounter = 0 Dim rowCounter As Integer = 0 Dim rowSum As Integer = DataGridView1.ColumnHeadersHeight For r As Integer = 0 To DataGridView1.Rows.Count - 2 If rowSum + DataGridView1.Rows(r).Height < maxHeight Then rowSum += DataGridView1.Rows(r).Height rowCounter += 1 Else pages(pageCounter) = New pageDetails With {.columns = pages(pageCounter).columns, .rows = rowCounter, .startCol = pages(pageCounter).startCol, .startRow = pages(pageCounter).startRow} For x As Integer = 1 To maxPagesWide - 1 pages(pageCounter + x) = New pageDetails With {.columns = pages(pageCounter + x).columns, .rows = rowCounter, .startCol = pages(pageCounter + x).startCol, .startRow = pages(pageCounter).startRow} Next pageCounter += maxPagesWide For x As Integer = 0 To maxPagesWide - 1 pages.Add(pageCounter + x, New pageDetails With {.columns = pages(x).columns, .rows = 0, .startCol = pages(x).startCol, .startRow = r}) Next rowSum = DataGridView1.ColumnHeadersHeight + DataGridView1.Rows(r).Height rowCounter = 1 End If If r = DataGridView1.Rows.Count - 2 Then For x As Integer = 0 To maxPagesWide - 1 If pages(pageCounter + x).rows = 0 Then pages(pageCounter + x) = New pageDetails With {.columns = pages(pageCounter + x).columns, .rows = rowCounter, .startCol = pages(pageCounter + x).startCol, .startRow = pages(pageCounter + x).startRow} End If Next End If Next maxPagesTall = pages.Count \ maxPagesWide End Sub
''' <summary> ''' this is the actual printing routine. ''' using the pagedetails i calculated earlier, it prints a title, ''' + as much of the datagridview as will fit on 1 page, then moves to the next page. ''' this is setup to be dynamic. try resizing the dgv columns or rows ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage Dim rect As New Rectangle(20, 20, CInt(PrintDocument1.DefaultPageSettings.PrintableArea.Width), Label1.Height) Dim sf As New StringFormat sf.Alignment = StringAlignment.Center sf.LineAlignment = StringAlignment.Center 'truncate text overflow 'sf.Trimming = StringTrimming.EllipsisCharacter 'sf.FormatFlags = StringFormatFlags.NoWrap e.Graphics.DrawString(Label1.Text, Label1.Font, Brushes.Black, rect, sf) sf.Alignment = StringAlignment.Near Dim startX As Integer = 50 Dim startY As Integer = rect.Bottom Static startPage As Integer = 0 For p As Integer = startPage To pages.Count - 1 Dim cell As New Rectangle(startX, startY, DataGridView1.RowHeadersWidth, DataGridView1.ColumnHeadersHeight) e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell) e.Graphics.DrawRectangle(Pens.Black, cell) startY += DataGridView1.ColumnHeadersHeight For r As Integer = pages(p).startRow To pages(p).startRow + pages(p).rows - 1 cell = New Rectangle(startX, startY, DataGridView1.RowHeadersWidth, DataGridView1.Rows(r).Height) e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell) e.Graphics.DrawRectangle(Pens.Black, cell) e.Graphics.DrawString(DataGridView1.Rows(r).HeaderCell.Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf) startY += DataGridView1.Rows(r).Height Next startX += cell.Width startY = rect.Bottom For c As Integer = pages(p).startCol To pages(p).startCol + pages(p).columns - 1 cell = New Rectangle(startX, startY, DataGridView1.Columns(c).Width, DataGridView1.ColumnHeadersHeight) e.Graphics.FillRectangle(New SolidBrush(SystemColors.ControlLight), cell) e.Graphics.DrawRectangle(Pens.Black, cell) e.Graphics.DrawString(DataGridView1.Columns(c).HeaderCell.Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf) startX += DataGridView1.Columns(c).Width Next startY = rect.Bottom + DataGridView1.ColumnHeadersHeight For r As Integer = pages(p).startRow To pages(p).startRow + pages(p).rows - 1 startX = 50 + DataGridView1.RowHeadersWidth For c As Integer = pages(p).startCol To pages(p).startCol + pages(p).columns - 1 cell = New Rectangle(startX, startY, DataGridView1.Columns(c).Width, DataGridView1.Rows(r).Height) e.Graphics.DrawRectangle(Pens.Black, cell) e.Graphics.DrawString(DataGridView1(c, r).Value.ToString, DataGridView1.Font, Brushes.Black, cell, sf) startX += DataGridView1.Columns(c).Width Next startY += DataGridView1.Rows(r).Height Next If p <> pages.Count - 1 Then startPage = p + 1 e.HasMorePages = True Return Else startPage = 0 End If Next End Sub
Simple Multi-Page DataGridView Printing
Simple Multi-Page DataGridView Printing (Landscape)
The standard .Net DataGridView is a fairly versatile customizable control, that allows viewing and editing tables of data which can be either bound or unbound. The ExtendedDataGridView adds some extra functionality to the standard .Net DataGridView making it easier to use in the case of the extra Events, and the printing and exporting as CSV, further enhance the control.
In a simple application that binds data to the DataGridView, it's slightly more difficult to use the ExtendedDataGridView, but not impossible by any means. The issue is, if you allow the DataGridView to AutogenerateColumns, it will use the standard DataGridViewColumns. Fortunately changing column types in code is a simple task, or you can set AutogenerateColumns to False and bind your columns manually.
This is an extended DataGridView control that adds three public methods, a ContextMenuStrip, and two custom Events to a standard DataGridView control.
The three methods are:
with the purposes being fairly self-explanatory.
The ContextMenuStrip has MenuItems for invoking those three methods and a Reveal MenuItem which hosts a UserControl used for revealing hidden columns.
The (DataGridView ) control uses custom columns which provide an extra property to each extended version of the standard DataGridView columns, allowing selection of two custom HeaderCells for the extended DataGridViewCheckBoxColumn, which are:
along with:
Again the purposes of the HeaderCells are self-explanatory.
All of the other extended DataGridViewColumns don't have the CheckAll option.
The Printer class called by the Print method is optimized to print any combination of columns and rows and also prints visual elements, such as ComboBoxes, CheckBoxes, Images, Links, and Cell backcolors.
Both the Print and the PreviewPrint methods use a custom PrintDialog, which allows selection of a printer, page ranges (FromPage, ToPage ), page orientation, number of copies and collating pages in multiple copy print runs.
The SaveasCSV option allows exporting the DataGridView contents as a CSV file.
The custom Events provided by the extended DataGridView are:
cellCheckedChanged - which exposes these properties in the DataGridViewCheckBoxCell:
cellSelectedIndexChanged - which exposes these properties in the DataGridViewComboBoxCell:
Imports System.ComponentModel Public Class altDataGridViewCheckBoxColumn Inherits DataGridViewCheckBoxColumn Private _HeaderStyle As enumerations.style2 <Category("Design"), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)> Public Property HeaderStyle() As enumerations.style2 Get Return _HeaderStyle End Get Set(ByVal value As enumerations.style2) _HeaderStyle = value Select Case _HeaderStyle Case style2.Standard MyBase.HeaderCell = New DataGridViewColumnHeaderCell Case style2.CheckAll MyBase.HeaderCell = New checkAllHeaderCell Case style2.HideColumn MyBase.HeaderCell = New checkHideColumnHeaderCell End Select End Set End Property Public Overrides Function clone() As Object Dim copyColumn As altDataGridViewCheckBoxColumn = DirectCast(MyBase.Clone, altDataGridViewCheckBoxColumn) copyColumn._HeaderStyle = Me.HeaderStyle Return copyColumn End Function End Class
Overall, the ExtendedDataGridView provides a simpler experience for the developer. Two issues that are queried regularly are how to capture CheckedChanged events and how to get more information about the ComboBox selection in a DataGridView. The ExtendedDataGridView simplifies both of these issues by providing two custom Events.
Another two issues regularly asked are how to add a CheckBox to a HeaderCell. This is another addition included in the ExtendedDataGridView. DataGridViewCheckBoxColumns HeaderCells allow CheckAll CheckBoxes or HideColumn CheckBoxes, and all of the other extended column types allow just HideColumn CheckBoxes in the HeaderCell.
Also, Printing is a major hurdle for a developer utilizing a DataGridView. In the ExtendedDataGridView control, that is a built-in feature. To print your DataGridView, you just need to call it's Print() method. Similarly, you can preview your printing by calling the PreviewPrint() method.
Finally, you can also export to CSV by calling the SaveasCSV() method.
These additional features enable faster application development without leaving out valuable functionality...
Advanced Multi-Page DataGridView Printing