It might help some people: here is the VB.Net code
Imports System
Imports System.Text
Imports System.Collections
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Drawing.Printing
Imports System.Data
Imports System.Globalization
Imports System.Windows.Forms
'[module:CLSCompliant(true)]
Namespace DGVPrinterHelper
'AllocationRequest
''' <summary>
''' Data Grid View Printer. Print functions for a datagridview, since MS
''' didn't see fit to do it.
''' </summary>
Class DGVPrinter
'---------------------------------------------------------------------
' internal classes/structs
'---------------------------------------------------------------------
' handle wide-column printing - that is, lists of columns that extend
' wider than one page width. Columns are broken up into "Page Sets" that
' are printed one after another until all columns are printed.
Private Class PageDef
Public Sub New(ByVal m As Margins, ByVal count As Integer)
colstoprint = New List(Of Object)(count)
colwidths = New List(Of Single)(count)
colwidthsoverride = New List(Of Single)(count)
coltotalwidth = 0
margins = DirectCast(m.Clone(), Margins)
End Sub
Public colstoprint As IList
Public colwidths As List(Of Single)
Public colwidthsoverride As List(Of Single)
Public coltotalwidth As Single
Public margins As Margins
End Class
Private pagesets As IList(Of PageDef)
Private currentpageset As Integer = 0
' class to hold settings for the PrintDialog presented to the user during
' the print process
Public Class PrintDialogSettingsClass
Public AllowSelection As Boolean = True
Public AllowSomePages As Boolean = True
Public AllowCurrentPage As Boolean = True
Public AllowPrintToFile As Boolean = False
Public ShowHelp As Boolean = True
Public ShowNetwork As Boolean = True
End Class
'---------------------------------------------------------------------
' global variables
'---------------------------------------------------------------------
#Region "global variables"
' the data grid view we're printing
Private dgv As DataGridView = Nothing
' print document
Private printDoc As PrintDocument = Nothing
' print status items
Private rowstoprint As IList
Private colstoprint As IList
' divided into pagesets for printing
Private lastrowprinted As Integer = -1
Private fromPage As Integer = 0
Private toPage As Integer = -1
' page formatting options
Private pageHeight As Integer = 0
Private pageWidth As Integer = 0
Private printWidth As Integer = 0
Private rowheaderwidth As Single = 0
Private CurrentPage As Integer = 0
Private printRange As PrintRange
' calculated values
Private headerHeight As Single = 0
Private footerHeight As Single = 0
Private pagenumberHeight As Single = 0
Private colheaderheight As Single = 0
Private rowheights As List(Of Single)
Private colwidths As List(Of Single)
#End Region
'---------------------------------------------------------------------
' properties - settable by user
'---------------------------------------------------------------------
#Region "properties"
#Region "global properties"
''' <summary>
''' expose printer settings to allow access to calling program
''' </summary>
Public ReadOnly Property PrintSettings() As PrinterSettings
Get
Return printDoc.PrinterSettings
End Get
End Property
''' <summary>
''' expose settings for the PrintDialog displayed to the user
''' </summary>
Private m_printDialogSettings As New PrintDialogSettingsClass()
Public ReadOnly Property PrintDialogSettings() As PrintDialogSettingsClass
Get
Return m_printDialogSettings
End Get
End Property
''' <summary>
''' Set Printer Name
''' </summary>
Private m_printerName As String
Public Property PrinterName() As String
Get
Return m_printerName
End Get
Set(ByVal value As String)
m_printerName = value
End Set
End Property
''' <summary>
''' Allow access to the underlying print document
''' </summary>
Public ReadOnly Property printDocument() As PrintDocument
Get
Return printDoc
End Get
End Property
''' <summary>
''' Allow caller to set the upper-left corner icon used
''' in the print preview dialog
''' </summary>
Private ppvIcon As Icon = Nothing
Public Property PreviewDialogIcon() As Icon
Get
Return ppvIcon
End Get
Set(ByVal value As Icon)
ppvIcon = value
End Set
End Property
#End Region
' Title
#Region "title properties"
' override flag
Private overridetitleformat As Boolean = False
''' <summary>
''' Title for this report. Default is empty.
''' </summary>
Private m_title As String
Public Property Title() As String
Get
Return m_title
End Get
Set(ByVal value As String)
m_title = value
If m_docName Is Nothing Then
printDoc.DocumentName = value
End If
End Set
End Property
''' <summary>
''' Name of the document. Default is report title (can be empty)
''' </summary>
Private m_docName As String
Public Property DocName() As String
Get
Return m_docName
End Get
Set(ByVal value As String)
printDoc.DocumentName = value
m_docName = value
End Set
End Property
''' <summary>
''' Font for the title. Default is Tahoma, 18pt.
''' </summary>
Private m_titlefont As Font
Public Property TitleFont() As Font
Get
Return m_titlefont
End Get
Set(ByVal value As Font)
m_titlefont = value
End Set
End Property
''' <summary>
''' Foreground color for the title. Default is Black
''' </summary>
Private m_titlecolor As Color
Public Property TitleColor() As Color
Get
Return m_titlecolor
End Get
Set(ByVal value As Color)
m_titlecolor = value
End Set
End Property
''' <summary>
''' Allow override of the header cell format object
''' </summary>
Private m_titleformat As StringFormat
Public Property TitleFormat() As StringFormat
Get
Return m_titleformat
End Get
Set(ByVal value As StringFormat)
m_titleformat = value
overridetitleformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the title string alignment. Default value is
''' Alignment - Near;
''' </summary>
Public Property TitleAlignment() As StringAlignment
Get
Return m_titleformat.Alignment
End Get
Set(ByVal value As StringAlignment)
m_titleformat.Alignment = value
overridetitleformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the title string format flags. Default values
''' are: FormatFlags - NoWrap, LineLimit, NoClip
''' </summary>
Public Property TitleFormatFlags() As StringFormatFlags
Get
Return m_titleformat.FormatFlags
End Get
Set(ByVal value As StringFormatFlags)
m_titleformat.FormatFlags = value
overridetitleformat = True
End Set
End Property
#End Region
' SubTitle
#Region "subtitle properties"
' override flat
Private overridesubtitleformat As Boolean = False
''' <summary>
''' SubTitle for this report. Default is empty.
''' </summary>
Private m_subtitle As String
Public Property SubTitle() As String
Get
Return m_subtitle
End Get
Set(ByVal value As String)
m_subtitle = value
End Set
End Property
''' <summary>
''' Font for the subtitle. Default is Tahoma, 12pt.
''' </summary>
Private m_subtitlefont As Font
Public Property SubTitleFont() As Font
Get
Return m_subtitlefont
End Get
Set(ByVal value As Font)
m_subtitlefont = value
End Set
End Property
''' <summary>
''' Foreground color for the subtitle. Default is Black
''' </summary>
Private m_subtitlecolor As Color
Public Property SubTitleColor() As Color
Get
Return m_subtitlecolor
End Get
Set(ByVal value As Color)
m_subtitlecolor = value
End Set
End Property
''' <summary>
''' Allow override of the header cell format object
''' </summary>
Private m_subtitleformat As StringFormat
Public Property SubTitleFormat() As StringFormat
Get
Return m_subtitleformat
End Get
Set(ByVal value As StringFormat)
m_subtitleformat = value
overridesubtitleformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the subtitle string alignment. Default value is
''' Alignment - Near;
''' </summary>
Public Property SubTitleAlignment() As StringAlignment
Get
Return m_subtitleformat.Alignment
End Get
Set(ByVal value As StringAlignment)
m_subtitleformat.Alignment = value
overridesubtitleformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the subtitle string format flags. Default values
''' are: FormatFlags - NoWrap, LineLimit, NoClip
''' </summary>
Public Property SubTitleFormatFlags() As StringFormatFlags
Get
Return m_subtitleformat.FormatFlags
End Get
Set(ByVal value As StringFormatFlags)
m_subtitleformat.FormatFlags = value
overridesubtitleformat = True
End Set
End Property
#End Region
' Footer
#Region "footer properties"
' override flag
Private overridefooterformat As Boolean = False
''' <summary>
''' footer for this report. Default is empty.
''' </summary>
Private m_footer As String
Public Property Footer() As String
Get
Return m_footer
End Get
Set(ByVal value As String)
m_footer = value
End Set
End Property
''' <summary>
''' Font for the footer. Default is Tahoma, 10pt.
''' </summary>
Private m_footerfont As Font
Public Property FooterFont() As Font
Get
Return m_footerfont
End Get
Set(ByVal value As Font)
m_footerfont = value
End Set
End Property
''' <summary>
''' Foreground color for the footer. Default is Black
''' </summary>
Private m_footercolor As Color
Public Property FooterColor() As Color
Get
Return m_footercolor
End Get
Set(ByVal value As Color)
m_footercolor = value
End Set
End Property
''' <summary>
''' Allow override of the header cell format object
''' </summary>
Private m_footerformat As StringFormat
Public Property FooterFormat() As StringFormat
Get
Return m_footerformat
End Get
Set(ByVal value As StringFormat)
m_footerformat = value
overridefooterformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the footer string alignment. Default value is
''' Alignment - Center;
''' </summary>
Public Property FooterAlignment() As StringAlignment
Get
Return m_footerformat.Alignment
End Get
Set(ByVal value As StringAlignment)
m_footerformat.Alignment = value
overridefooterformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the footer string format flags. Default values
''' are: FormatFlags - NoWrap, LineLimit, NoClip
''' </summary>
Public Property FooterFormatFlags() As StringFormatFlags
Get
Return m_footerformat.FormatFlags
End Get
Set(ByVal value As StringFormatFlags)
m_footerformat.FormatFlags = value
overridefooterformat = True
End Set
End Property
Private m_footerspacing As Single
Public Property FooterSpacing() As Single
Get
Return m_footerspacing
End Get
Set(ByVal value As Single)
m_footerspacing = value
End Set
End Property
#End Region
' Page Numbering
#Region "page number properties"
' override flag
Private overridepagenumberformat As Boolean = False
''' <summary>
''' Include page number in the printout. Default is true.
''' </summary>
Private pageno As Boolean = True
Public Property PageNumbers() As Boolean
Get
Return pageno
End Get
Set(ByVal value As Boolean)
pageno = value
End Set
End Property
''' <summary>
''' Font for the page number, Default is Tahoma, 8pt.
''' </summary>
Private pagenofont As Font
Public Property PageNumberFont() As Font
Get
Return pagenofont
End Get
Set(ByVal value As Font)
pagenofont = value
End Set
End Property
''' <summary>
''' Text color (foreground) for the page number. Default is Black
''' </summary>
Private pagenocolor As Color
Public Property PageNumberColor() As Color
Get
Return pagenocolor
End Get
Set(ByVal value As Color)
pagenocolor = value
End Set
End Property
''' <summary>
''' Allow override of the header cell format object
''' </summary>
Private m_pagenumberformat As StringFormat
Public Property PageNumberFormat() As StringFormat
Get
Return m_pagenumberformat
End Get
Set(ByVal value As StringFormat)
m_pagenumberformat = value
overridepagenumberformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the page number string alignment. Default value is
''' Alignment - Near;
''' </summary>
Public Property PageNumberAlignment() As StringAlignment
Get
Return m_pagenumberformat.Alignment
End Get
Set(ByVal value As StringAlignment)
m_pagenumberformat.Alignment = value
overridepagenumberformat = True
End Set
End Property
''' <summary>
''' Allow the user to override the pagenumber string format flags. Default values
''' are: FormatFlags - NoWrap, LineLimit, NoClip
''' </summary>
Public Property PageNumberFormatFlags() As StringFormatFlags
Get
Return m_pagenumberformat.FormatFlags
End Get
Set(ByVal value As StringFormatFlags)
m_pagenumberformat.FormatFlags = value
overridepagenumberformat = True
End Set
End Property
''' <summary>
''' Allow the user to select whether to have the page number at the top or bottom
''' of the page. Default is false: page numbers on the bottom of the page
''' </summary>
Private pagenumberontop As Boolean = False
Public Property PageNumberInHeader() As Boolean
Get
Return pagenumberontop
End Get
Set(ByVal value As Boolean)
pagenumberontop = value
End Set
End Property
''' <summary>
''' Should the page number be printed on a separate line, or printed on the
''' same line as the header / footer? Default is false;
''' </summary>
Private m_pagenumberonseparateline As Boolean = False
Public Property PageNumberOnSeparateLine() As Boolean
Get
Return m_pagenumberonseparateline
End Get
Set(ByVal value As Boolean)
m_pagenumberonseparateline = value
End Set
End Property
''' <summary>
''' Show the total page number as n of total
''' </summary>
Private totalpages As Integer
Private m_showtotalpagenumber As Boolean = False
Public Property ShowTotalPageNumber() As Boolean
Get
Return m_showtotalpagenumber
End Get
Set(ByVal value As Boolean)
m_showtotalpagenumber = value
End Set
End Property
''' <summary>
''' Text separating page number and total page number. Default is ' of '.
''' </summary>
Private m_pageseparator As String = " of "
Public Property PageSeparator() As String
Get
Return m_pageseparator
End Get
Set(ByVal value As String)
m_pageseparator = value
End Set
End Property
Private m_pagetext As String = "Page "
Public Property PageText() As String
Get
Return m_pagetext
End Get
Set(ByVal value As String)
m_pagetext = value
End Set
End Property
Private m_parttext As String = " - Part "
Public Property PartText() As String
Get
Return m_parttext
End Get
Set(ByVal value As String)
m_parttext = value
End Set
End Property
#End Region
' Header Cell Printing
#Region "header cell properties"
''' <summary>
''' Allow override of the header cell format object
''' </summary>
Private headercellformat As StringFormat = Nothing
Public Function GetHeaderCellFormat(ByVal grid As DataGridView) As StringFormat
buildstringformat(headercellformat, grid.Columns(0).HeaderCell.InheritedStyle, m_headercellalignment, StringAlignment.Near, m_headercellformatflags, StringTrimming.Word)
Return headercellformat
End Function
''' <summary>
''' Deprecated - use HeaderCellFormat
''' Allow the user to override the header cell string alignment. Default value is
''' Alignment - Near;
''' </summary>
Private m_headercellalignment As StringAlignment
Public Property HeaderCellAlignment() As StringAlignment
Get
Return m_headercellalignment
End Get
Set(ByVal value As StringAlignment)
m_headercellalignment = value
End Set
End Property
''' <summary>
''' Deprecated - use HeaderCellFormat
''' Allow the user to override the header cell string format flags. Default values
''' are: FormatFlags - NoWrap, LineLimit, NoClip
''' </summary>
Private m_headercellformatflags As StringFormatFlags
Public Property HeaderCellFormatFlags() As StringFormatFlags
Get
Return m_headercellformatflags
End Get
Set(ByVal value As StringFormatFlags)
m_headercellformatflags = value
End Set
End Property
#End Region
' Individual Cell Printing
#Region "cell properties"
''' <summary>
''' Allow override of the cell printing format
''' </summary>
Private cellformat As StringFormat = Nothing
Public Function GetCellFormat(ByVal grid As DataGridView) As StringFormat
buildstringformat(cellformat, grid.Rows(0).Cells(0).InheritedStyle, m_cellalignment, StringAlignment.Near, m_cellformatflags, StringTrimming.Word)
Return cellformat
End Function
''' <summary>
''' Deprecated - use GetCellFormat
''' Allow the user to override the cell string alignment. Default value is
''' Alignment - Near;
''' </summary>
Private m_cellalignment As StringAlignment
Public Property CellAlignment() As StringAlignment
Get
Return m_cellalignment
End Get
Set(ByVal value As StringAlignment)
m_cellalignment = value
End Set
End Property
''' <summary>
''' Deprecated - use GetCellFormat
''' Allow the user to override the cell string format flags. Default values
''' are: FormatFlags - NoWrap, LineLimit, NoClip
''' </summary>
Private m_cellformatflags As StringFormatFlags
Public Property CellFormatFlags() As StringFormatFlags
Get
Return m_cellformatflags
End Get
Set(ByVal value As StringFormatFlags)
m_cellformatflags = value
End Set
End Property
''' <summary>
''' allow the user to override the column width calcs with their own defaults
''' </summary>
Private colwidthsoverride As New List(Of Single)()
Private publicwidthoverrides As New Dictionary(Of String, Single)()
Public ReadOnly Property ColumnWidths() As Dictionary(Of String, Single)
Get
Return publicwidthoverrides
End Get
End Property
''' <summary>
''' Allow per column style overrides
''' </summary>
Private colstyles As New Dictionary(Of String, DataGridViewCellStyle)()
Public ReadOnly Property ColumnStyles() As Dictionary(Of String, DataGridViewCellStyle)
Get
Return colstyles
End Get
End Property
#End Region
' Page Level Properties
#Region "page level properties"
''' <summary>
''' Page margins override. Default is (60, 60, 40, 40)
''' </summary>
Private m_printmargins As Margins
Public Property PrintMargins() As Margins
Get
Return m_printmargins
End Get
Set(ByVal value As Margins)
m_printmargins = value
End Set
End Property
''' <summary>
''' Expose the printdocument default page settings to the caller
''' </summary>
Public ReadOnly Property PageSettings() As PageSettings
Get
Return printDoc.DefaultPageSettings
End Get
End Property
''' <summary>
''' Spread the columns porportionally accross the page. Default is false.
''' </summary>
Private m_porportionalcolumns As Boolean = False
Public Property PorportionalColumns() As Boolean
Get
Return m_porportionalcolumns
End Get
Set(ByVal value As Boolean)
m_porportionalcolumns = value
End Set
End Property
''' <summary>
''' Center the table on the page.
''' </summary>
Public Enum Alignment
NotSet
Left
Right
Center
End Enum
Private m_tablealignment As Alignment = Alignment.NotSet
Public Property TableAlignment() As Alignment
Get
Return m_tablealignment
End Get
Set(ByVal value As Alignment)
m_tablealignment = value
End Set
End Property
#End Region
#End Region
''' <summary>
''' Constructor for DGVPrinter
''' </summary>
Public Sub New()
' create print document
printDoc = New PrintDocument()
AddHandler printDoc.PrintPage, AddressOf printDoc_PrintPage
AddHandler printDoc.BeginPrint, AddressOf printDoc_BeginPrint
m_printmargins = New Margins(60, 60, 40, 40)
' set default fonts
pagenofont = New Font("Tahoma", 8, FontStyle.Regular, GraphicsUnit.Point)
pagenocolor = Color.Black
m_titlefont = New Font("Tahoma", 18, FontStyle.Bold, GraphicsUnit.Point)
m_titlecolor = Color.Black
m_subtitlefont = New Font("Tahoma", 12, FontStyle.Bold, GraphicsUnit.Point)
m_subtitlecolor = Color.Black
m_footerfont = New Font("Tahoma", 10, FontStyle.Bold, GraphicsUnit.Point)
m_footercolor = Color.Black
' Create string formatting objects
buildstringformat(m_titleformat, Nothing, StringAlignment.Center, StringAlignment.Center, StringFormatFlags.NoWrap Or StringFormatFlags.LineLimit Or StringFormatFlags.NoClip, StringTrimming.Word)
buildstringformat(m_subtitleformat, Nothing, StringAlignment.Center, StringAlignment.Center, StringFormatFlags.NoWrap Or StringFormatFlags.LineLimit Or StringFormatFlags.NoClip, StringTrimming.Word)
buildstringformat(m_footerformat, Nothing, StringAlignment.Center, StringAlignment.Center, StringFormatFlags.NoWrap Or StringFormatFlags.LineLimit Or StringFormatFlags.NoClip, StringTrimming.Word)
buildstringformat(m_pagenumberformat, Nothing, StringAlignment.Far, StringAlignment.Center, StringFormatFlags.NoWrap Or StringFormatFlags.LineLimit Or StringFormatFlags.NoClip, StringTrimming.Word)
' Set these formatting objects to null to flag whether or not they were set by the caller
headercellformat = Nothing
cellformat = Nothing
' Deprecated properties - retain for backwards compatibility
m_headercellalignment = StringAlignment.Near
m_headercellformatflags = StringFormatFlags.LineLimit Or StringFormatFlags.NoClip
m_cellalignment = StringAlignment.Near
m_cellformatflags = StringFormatFlags.LineLimit Or StringFormatFlags.NoClip
End Sub
'---------------------------------------------------------------------
'---------------------------------------------------------------------
' Primary Interface - Presents a dialog and then prints or previews the
' indicated data grid view
'---------------------------------------------------------------------
'---------------------------------------------------------------------
''' <summary>
''' Start the printing process, print to a printer.
''' </summary>
''' <param name="dgv">The DataGridView to print</param>
''' NOTE: Any changes to this method also need to be done in PrintPreviewDataGridView
Public Sub PrintDataGridView(ByVal dgv As DataGridView)
' save the datagridview we're printing
Me.dgv = dgv
' display dialog and print
If DialogResult.OK = DisplayPrintDialog() Then
SetupPrint()
printDoc.Print()
End If
End Sub
''' <summary>
''' Start the printing process, print to a print preview dialog
''' </summary>
''' <param name="dgv">The DataGridView to print</param>
''' NOTE: Any changes to this method also need to be done in PrintDataGridView
Public Sub PrintPreviewDataGridView(ByVal dgv As DataGridView)
' save the datagridview we're printing
Me.dgv = dgv
' display dialog and print
If DialogResult.OK = DisplayPrintDialog() Then
SetupPrint()
Dim ppdialog As New PrintPreviewDialog()
ppdialog.Document = printDoc
If ppvIcon IsNot Nothing Then
ppdialog.Icon = ppvIcon
End If
ppdialog.ShowDialog()
End If
End Sub
'---------------------------------------------------------------------
'---------------------------------------------------------------------
' Alternative Interface. In order to set the print information correctly
' either the DisplayPrintDialog() routine must be called, OR the
' PrintDocument (and PrinterSettings) must be handled through calling
' PrintDialog separately.
'
' Once the PrintDocument has been setup, the PrintNoDisplay() and/or
' PrintPreviewNoDisplay() routines can be called to print multiple
' DataGridViews using the same print setup.
'---------------------------------------------------------------------
'---------------------------------------------------------------------
''' <summary>
''' Display a printdialog and return the result. Either this method or
''' the equivalent must be done prior to calling either of the PrintNoDisplay
''' or PrintPreviewNoDisplay methods.
''' </summary>
''' <returns></returns>
Public Function DisplayPrintDialog() As DialogResult
' create new print dialog and set options
Dim pd As New PrintDialog()
pd.AllowSelection = m_printDialogSettings.AllowSelection
pd.AllowSomePages = m_printDialogSettings.AllowSomePages
pd.AllowCurrentPage = m_printDialogSettings.AllowCurrentPage
pd.AllowPrintToFile = m_printDialogSettings.AllowPrintToFile
pd.ShowHelp = m_printDialogSettings.ShowHelp
pd.ShowNetwork = m_printDialogSettings.ShowNetwork
' setup print dialog with internal setttings
pd.Document = printDoc
printDoc.DefaultPageSettings.Margins = m_printmargins
If Not [String].IsNullOrEmpty(m_printerName) Then
printDoc.PrinterSettings.PrinterName = m_printerName
End If
' show the dialog and display the result
Return pd.ShowDialog()
End Function
''' <summary>
''' Print the provided grid view. Either DisplayPrintDialog() or it's equivalent
''' setup must be completed prior to calling this routine
''' </summary>
''' <param name="dgv"></param>
Public Sub PrintNoDisplay(ByVal dgv As DataGridView)
' save the grid we're printing
Me.dgv = dgv
' setup and do printing
SetupPrint()
printDoc.Print()
End Sub
''' <summary>
''' Preview the provided grid view. Either DisplayPrintDialog() or it's equivalent
''' setup must be completed prior to calling this routine
''' </summary>
''' <param name="dgv"></param>
Public Sub PrintPreviewNoDisplay(ByVal dgv As DataGridView)
' save the grid we're printing
Me.dgv = dgv
' display the preview dialog
SetupPrint()
Dim ppdialog As New PrintPreviewDialog()
ppdialog.Document = printDoc
If ppvIcon IsNot Nothing Then
ppdialog.Icon = ppvIcon
End If
ppdialog.ShowDialog()
End Sub
'---------------------------------------------------------------------
'---------------------------------------------------------------------
' Internal Methods
'---------------------------------------------------------------------
'---------------------------------------------------------------------
''' Set up the print job. Save information from print dialog
''' and print document for easy access. Also sets up the rows
''' and columns that will be printed. At this point, we're
''' collecting all columns in colstoprint. This will be broken
''' up into pagesets later on
Private Sub SetupPrint()
'-----------------------------------------------------------------
' Set headercell and normal cell print formats if they were not
' explicitly set by the caller
'-----------------------------------------------------------------
If headercellformat Is Nothing Then
buildstringformat(headercellformat, dgv.Columns(0).HeaderCell.InheritedStyle, m_headercellalignment, StringAlignment.Near, m_headercellformatflags, StringTrimming.Word)
End If
If cellformat Is Nothing Then
buildstringformat(cellformat, dgv.DefaultCellStyle, m_cellalignment, StringAlignment.Near, m_cellformatflags, StringTrimming.Word)
End If
'-----------------------------------------------------------------
' get info on the limits of the printer's actual print area available. Convert
' to int's to work with margins.
'-----------------------------------------------------------------
Dim hardx As Integer = CInt(Math.Round(printDoc.DefaultPageSettings.HardMarginX))
Dim hardy As Integer = CInt(Math.Round(printDoc.DefaultPageSettings.HardMarginY))
Dim printareawidth As Integer
If printDoc.DefaultPageSettings.Landscape Then
printareawidth = CInt(Math.Round(printDoc.DefaultPageSettings.PrintableArea.Height))
Else
printareawidth = CInt(Math.Round(printDoc.DefaultPageSettings.PrintableArea.Width))
End If
'-----------------------------------------------------------------
' set the print area we're working within
'-----------------------------------------------------------------
pageHeight = printDoc.DefaultPageSettings.Bounds.Height
pageWidth = printDoc.DefaultPageSettings.Bounds.Width
'-----------------------------------------------------------------
' Set the printable area: margins and pagewidth
'-----------------------------------------------------------------
' Set initial printer margins
m_printmargins = printDoc.DefaultPageSettings.Margins
' adjust for when the margins are less than the printer's hard x/y limits
If hardx > m_printmargins.Right Then m_printmargins.Right = hardx
If hardx > m_printmargins.Left Then m_printmargins.Left = hardx
If hardy > m_printmargins.Top Then m_printmargins.Top = hardy
If hardy > m_printmargins.Bottom Then m_printmargins.Bottom = hardy
' Now, we can calc default print width, again, respecting the printer's limitations
printWidth = pageWidth - m_printmargins.Left - m_printmargins.Right
If printWidth > printareawidth Then printWidth = printareawidth
'-----------------------------------------------------------------
' Figure out which pages / rows to print
'-----------------------------------------------------------------
' save print range
printRange = printDoc.PrinterSettings.PrintRange
' pages to print handles "some pages" option
If printRange.SomePages = printRange Then
' set limits to only print some pages
fromPage = printDoc.PrinterSettings.FromPage
toPage = printDoc.PrinterSettings.ToPage
Else
' set extremes so that we'll print all pages
fromPage = 0
toPage = 2147483647
End If
'-----------------------------------------------------------------
' set up the rows and columns to print
'
' Note: The "Selectedxxxx" lists in the datagridview are 'stacks' that
' have the selected items pushed in the *in the order they were selected*
' i.e. not the order you want to print them in!
'-----------------------------------------------------------------
' rows to print (handles "selection" and "current page" options
If printRange.Selection = printRange Then
Dim temprowstoprint As SortedList
Dim tempcolstoprint As SortedList
'if DGV has rows selected, it's easy, selected rows and all visible columns
If 0 <> dgv.SelectedRows.Count Then
' sort the rows into index order
temprowstoprint = New SortedList(dgv.SelectedRows.Count)
For Each row As DataGridViewRow In dgv.SelectedRows
temprowstoprint.Add(row.Index, row)
Next
Dim ie As IEnumerator = temprowstoprint.Values.GetEnumerator()
rowstoprint = New List(Of Object)(temprowstoprint.Count)
For Each item As Object In temprowstoprint.Values
rowstoprint.Add(item)
Next
colstoprint = New List(Of Object)(dgv.Columns.Count)
For Each col As DataGridViewColumn In dgv.Columns
If col.Visible Then
colstoprint.Add(col)
End If
Next
' if selected columns, then all rows, and selected columns
ElseIf 0 <> dgv.SelectedColumns.Count Then
rowstoprint = dgv.Rows
tempcolstoprint = New SortedList(dgv.SelectedColumns.Count)
For Each row As DataGridViewRow In dgv.SelectedColumns
tempcolstoprint.Add(row.Index, row)
Next
colstoprint = New List(Of Object)(tempcolstoprint.Count)
For Each item As Object In tempcolstoprint.Values
colstoprint.Add(item)
Next
Else
' we just have a bunch of selected cells so we have to do some work
' set up sorted lists. the selectedcells method does not guarantee
' that the cells will always be in left-right top-bottom order.
temprowstoprint = New SortedList(dgv.SelectedCells.Count)
tempcolstoprint = New SortedList(dgv.SelectedCells.Count)
' for each selected cell, add unique rows and columns
Dim colindex As Integer, rowindex As Integer
For Each cell As DataGridViewCell In dgv.SelectedCells
colindex = cell.ColumnIndex
rowindex = cell.RowIndex
' add unique rows
If Not temprowstoprint.Contains(rowindex) Then
temprowstoprint.Add(rowindex, dgv.Rows(rowindex))
End If
' add unique columns
If Not tempcolstoprint.Contains(colindex) Then
tempcolstoprint.Add(colindex, dgv.Columns(colindex))
End If
Next
' Move the now-duplicate free columns and rows to our list of what to print
rowstoprint = New List(Of Object)(temprowstoprint.Count)
For Each item As Object In temprowstoprint.Values
rowstoprint.Add(item)
Next
colstoprint = New List(Of Object)(tempcolstoprint.Count)
For Each item As Object In tempcolstoprint.Values
colstoprint.Add(item)
Next
End If
' if current page was selected, print visible columns for the
' displayed rows
ElseIf printRange.CurrentPage = printRange Then
' create lists
rowstoprint = New List(Of Object)(dgv.DisplayedRowCount(True))
colstoprint = New List(Of Object)(dgv.Columns.Count)
' select all visible rows on displayed page
For i As Integer = dgv.FirstDisplayedScrollingRowIndex To dgv.FirstDisplayedScrollingRowIndex + (dgv.DisplayedRowCount(True) - 1)
Dim row As DataGridViewRow = dgv.Rows(i)
If row.Visible Then
rowstoprint.Add(row)
End If
Next
' select all visible columns
colstoprint = New List(Of Object)(dgv.Columns.Count)
For Each col As DataGridViewColumn In dgv.Columns
If col.Visible Then
colstoprint.Add(col)
End If
Next
Else
' this is the default for print all - everything marked visible will be printed
' select all visible rows and all visible columns
rowstoprint = New List(Of Object)(dgv.Rows.Count)
For Each row As DataGridViewRow In dgv.Rows
If row.Visible Then
rowstoprint.Add(row)
End If
Next
colstoprint = New List(Of Object)(dgv.Columns.Count)
For Each col As DataGridViewColumn In dgv.Columns
If col.Visible Then
colstoprint.Add(col)
End If
Next
End If
' Reorder columns based on Display Index (if the programmer or user has
' changed the column display order we want to respect it in the printout)
Dim displayorderlist As New SortedList(colstoprint.Count)
For Each col As DataGridViewColumn In colstoprint
displayorderlist.Add(col.DisplayIndex, col)
Next
colstoprint.Clear()
For Each item As Object In displayorderlist.Values
colstoprint.Add(item)
Next
' Adjust override list to have the same number of entries as colstoprint
For Each col As DataGridViewColumn In colstoprint
If publicwidthoverrides.ContainsKey(col.Name) Then
colwidthsoverride.Add(publicwidthoverrides(col.Name))
Else
colwidthsoverride.Add(-1)
End If
Next
'-----------------------------------------------------------------
' Now that we know what we're printing, measure the print area and
' count the pages.
'-----------------------------------------------------------------
' Measure the print area
measureprintarea(printDoc.PrinterSettings.CreateMeasurementGraphics())
' Count the pages
totalpages = TotalPagess()
End Sub
Private Sub buildstringformat(ByRef format As StringFormat, ByVal controlstyle As DataGridViewCellStyle, ByVal alignment As StringAlignment, ByVal linealignment As StringAlignment, ByVal flags As StringFormatFlags, ByVal trim As StringTrimming)
' allocate format if it doesn't already exist
If format Is Nothing Then
format = New StringFormat()
End If
' Set defaults
format.Alignment = alignment
format.LineAlignment = linealignment
format.FormatFlags = flags
format.Trimming = trim
' use cell alignment to override defaulted alignments
If controlstyle IsNot Nothing Then
' Adjust the format based on the control settings, bias towards centered
Dim cellalign As DataGridViewContentAlignment = controlstyle.Alignment
If cellalign.ToString().Contains("Center") Then
format.Alignment = StringAlignment.Center
ElseIf cellalign.ToString().Contains("Left") Then
format.Alignment = StringAlignment.Near
ElseIf cellalign.ToString().Contains("Right") Then
format.Alignment = StringAlignment.Far
End If
If cellalign.ToString().Contains("Top") Then
format.LineAlignment = StringAlignment.Near
ElseIf cellalign.ToString().Contains("Middle") Then
format.LineAlignment = StringAlignment.Center
ElseIf cellalign.ToString().Contains("Bottom") Then
format.LineAlignment = StringAlignment.Far
End If
End If
End Sub
''' <summary>
''' Scan all the rows and columns to be printed and calculate the
''' overall individual column width (based on largest column value),
''' the header sizes, and determine all the row heights.
''' </summary>
''' <param name="g">The graphics context for all measurements</param>
Private Sub measureprintarea(ByVal g As Graphics)
Dim i As Integer, j As Integer
rowheights = New List(Of Single)(rowstoprint.Count)
colwidths = New List(Of Single)(colstoprint.Count)
headerHeight = 0
footerHeight = 0
' temp variables
Dim col As DataGridViewColumn
Dim row As DataGridViewRow
'-----------------------------------------------------------------
' measure the page headers and footers, including the grid column header cells
'-----------------------------------------------------------------
' measure the column headers
Dim headerfont As Font = dgv.ColumnHeadersDefaultCellStyle.Font
If headerfont Is Nothing Then
headerfont = dgv.DefaultCellStyle.Font
End If
' set initial column sizes based on column titles
For i = 0 To colstoprint.Count - 1
col = DirectCast(colstoprint(i), DataGridViewColumn)
' deal with overridden col widths
Dim usewidth As Single = 0
If 0 < colwidthsoverride(i) Then
usewidth = colwidthsoverride(i)
Else
usewidth = printWidth
End If
' measure the title for each column, keep widths and biggest height
Dim size As SizeF = g.MeasureString(col.HeaderText, headerfont, New SizeF(usewidth, 2147483647), headercellformat)
colwidths.Add(size.Width)
If colheaderheight < size.Height Then colheaderheight = size.Height
Next
'-----------------------------------------------------------------
' measure the page number
'-----------------------------------------------------------------
If pageno Then
pagenumberHeight = (g.MeasureString("Page", pagenofont, printWidth, m_pagenumberformat)).Height
End If
'-----------------------------------------------------------------
' Calc height of header.
' Header height is height of page number, title, subtitle and height of column headers
'-----------------------------------------------------------------
' note that we dont count the page number height if it's not on a separate line
If pagenumberontop AndAlso Not m_pagenumberonseparateline Then
headerHeight += pagenumberHeight
End If
If Not [String].IsNullOrEmpty(m_title) Then
headerHeight += (g.MeasureString(m_title, m_titlefont, printWidth, m_titleformat)).Height
End If
If Not [String].IsNullOrEmpty(m_subtitle) Then
headerHeight += (g.MeasureString(m_subtitle, m_subtitlefont, printWidth, m_subtitleformat)).Height
End If
headerHeight += colheaderheight
'-----------------------------------------------------------------
' measure the footer, if one is provided. Include the page number if we're printing
' it on the bottom
'-----------------------------------------------------------------
If Not [String].IsNullOrEmpty(m_footer) Then
footerHeight += (g.MeasureString(m_footer, m_footerfont, printWidth, m_footerformat)).Height
End If
' note we don't count the page number height if it's not on a separate line
If Not pagenumberontop AndAlso m_pagenumberonseparateline Then
footerHeight += pagenumberHeight
End If
footerHeight += m_footerspacing
'-----------------------------------------------------------------
' measure the grid to be printed ... this gets us all the row heights
' and an accurate measure of column widths for the printed area
'-----------------------------------------------------------------
For i = 0 To rowstoprint.Count - 1
row = DirectCast(rowstoprint(i), DataGridViewRow)
rowheights.Add(0)
' add row headers if they're visible
If dgv.RowHeadersVisible Then
Dim rhsize As SizeF = g.MeasureString(row.HeaderCell.EditedFormattedValue.ToString(), headerfont)
If rowheaderwidth < rhsize.Width Then rowheaderwidth = rhsize.Width
End If
' calculate widths for each column. We're looking for the largest width needed for
' all the rows of data.
For j = 0 To colstoprint.Count - 1
col = DirectCast(colstoprint(j), DataGridViewColumn)
' access the data to be printed - weird bug: had to move this up here since
' doing this access actually changes the cell's style. ???
Dim datastr As String = row.Cells(col.Index).EditedFormattedValue.ToString()
' get gridview style, and override if we have a set style for this column
Dim currentformat As StringFormat = Nothing
Dim colstyle As DataGridViewCellStyle = Nothing
If ColumnStyles.ContainsKey(col.Name) Then
colstyle = colstyles(col.Name)
' build the cell style and font
buildstringformat(currentformat, colstyle, cellformat.Alignment, cellformat.LineAlignment, cellformat.FormatFlags, cellformat.Trimming)
ElseIf (col.HasDefaultCellStyle) OrElse (row.Cells(col.Index).HasStyle) Then
colstyle = row.Cells(col.Index).InheritedStyle
' build the cell style and font
buildstringformat(currentformat, colstyle, cellformat.Alignment, cellformat.LineAlignment, cellformat.FormatFlags, cellformat.Trimming)
Else
currentformat = cellformat
colstyle = dgv.DefaultCellStyle
End If
' get the raw size of the string.
Dim size As SizeF = g.MeasureString(datastr, colstyle.Font)
' Handle fixed size cells and > printwidth cells where the width of the
' data won't fit. (I.E. need to stretch the row down the page)
If (0 < colwidthsoverride(j)) OrElse (size.Width > printWidth) Then
' set column width
If 0 < colwidthsoverride(j) Then
colwidths(j) = colwidthsoverride(j)
ElseIf size.Width > printWidth Then
colwidths(j) = printWidth
End If
' remeasure the string with the new limits and proper formatting for wrapping.
' Use an absurd height value so that we can get the real number of lines printed
Dim chars As Integer, lines As Integer
size = g.MeasureString(datastr, colstyle.Font, New SizeF(colwidths(j), 2147483647), currentformat, chars, lines)
' set row height
Dim tempheight As Single = size.Height
' lines * size.Height;
If rowheights(i) < tempheight Then rowheights(i) = tempheight
Else
If colwidths(j) < size.Width Then colwidths(j) = size.Width
If rowheights(i) < size.Height Then rowheights(i) = size.Height
End If
Next
Next
'-----------------------------------------------------------------
' Break the columns accross page sets. This is the key to printing
' where the total width is wider than one page.
'-----------------------------------------------------------------
' assume everything will fit on one page
pagesets = New List(Of PageDef)()
pagesets.Add(New PageDef(m_printmargins, colstoprint.Count))
Dim pset As Integer = 0
' Account for row headers
pagesets(pset).coltotalwidth = rowheaderwidth
' split columns into page sets
Dim columnwidth As Single
For i = 0 To colstoprint.Count - 1
' get initial column width
If colwidthsoverride(i) >= 0 Then columnwidth = colwidthsoverride(i)
' See if the column width takes us off the page - Except for the
' first column. This will prevent printing an empty page!! Otherwise,
' columns longer than the page width are printed on their own page
If printWidth < (pagesets(pset).coltotalwidth + columnwidth) AndAlso i <> 0 Then
pagesets.Add(New PageDef(m_printmargins, colstoprint.Count))
pset += 1
' Account for row headers
pagesets(pset).coltotalwidth = rowheaderwidth
End If
' update page set definition
pagesets(pset).colstoprint.Add(colstoprint(i))
pagesets(pset).colwidths.Add(colwidths(i))
pagesets(pset).colwidthsoverride.Add(colwidthsoverride(i))
pagesets(pset).coltotalwidth += columnwidth
Next
'-----------------------------------------------------------------
' Adjust column widths and table margins for each page
'-----------------------------------------------------------------
For i = 0 To pagesets.Count - 1
AdjustPageSets(g, pagesets(i))
Next
End Sub
''' <summary>
''' Adjust column widths for fixed and porportional columns, set the
''' margins to enforce the selected tablealignment.
''' </summary>
''' <param name="g">The graphics context for all measurements</param>
''' <param name="pageset">The pageset to adjust</param>
Private Sub AdjustPageSets(ByVal g As Graphics, ByVal pageset As PageDef)
Dim i As Integer
Dim fixedcolwidth As Single = rowheaderwidth
Dim remainingcolwidth As Single = 0
Dim ratio As Single
'-----------------------------------------------------------------
' Adjust the column widths in the page set to their final values,
' accounting for overridden widths and porportional column stretching
'-----------------------------------------------------------------
' calculate the amount of space reserved for fixed width columns
For i = 0 To pageset.colwidthsoverride.Count - 1
If pageset.colwidthsoverride(i) >= 0 Then
fixedcolwidth += pageset.colwidthsoverride(i)
End If
Next
' calculate the amount space for non-overridden columns
For i = 0 To pageset.colwidths.Count - 1
If pageset.colwidthsoverride(i) < 0 Then
remainingcolwidth += pageset.colwidths(i)
End If
Next
' calculate the ratio for porportional colums, use 1 for no
' non-overridden columns or not porportional
If m_porportionalcolumns AndAlso 0 < remainingcolwidth Then
ratio = (CSng(printWidth) - fixedcolwidth) / CSng(remainingcolwidth)
Else
ratio = CSng(1.0R)
End If
' reset all column widths for override and/or porportionality. coltotalwidth
' for each pageset should be <= pageWidth
pageset.coltotalwidth = rowheaderwidth
For i = 0 To pageset.colwidths.Count - 1
If pageset.colwidthsoverride(i) >= 0 Then
pageset.colwidths(i) = pageset.colwidthsoverride(i)
Else
pageset.colwidths(i) = pageset.colwidths(i) * ratio
End If
pageset.coltotalwidth += pageset.colwidths(i)
Next
'-----------------------------------------------------------------
' Table Alignment - now that we have the column widths established
' we can reset the table margins to get left, right and centered
' for the table on the page
'-----------------------------------------------------------------
' Reset Print Margins based on table alignment
If Alignment.Left = m_tablealignment Then
' Bias table to the left by setting "right" value
pageset.margins.Right = pageWidth - pageset.margins.Left - CInt(pageset.coltotalwidth)
If 0 > pageset.margins.Right Then
pageset.margins.Right = 0
End If
ElseIf Alignment.Right = m_tablealignment Then
' Bias table to the right by setting "left" value
pageset.margins.Left = pageWidth - pageset.margins.Right - CInt(pageset.coltotalwidth)
If 0 > pageset.margins.Left Then
pageset.margins.Left = 0
End If
ElseIf Alignment.Center = m_tablealignment Then
' Bias the table to the center by setting left and right equal
pageset.margins.Left = (pageWidth - CInt(pageset.coltotalwidth)) / 2
If 0 > pageset.margins.Left Then
pageset.margins.Left = 0
End If
pageset.margins.Right = pageset.margins.Left
End If
End Sub
''' <summary>
''' Count the pages that would be printed if print all was selected
''' </summary>
Private Function TotalPagess() As Integer
Dim pages As Integer = 1
Dim pos As Single = 0
Dim printablearea As Single = pageHeight - headerHeight - footerHeight - m_printmargins.Top - m_printmargins.Bottom
For i As Integer = 0 To (rowheights.Count) - 1
If pos + rowheights(i) > printablearea Then
' count the page
pages += 1
' reset the counter
pos = 0
End If
' add space
pos += rowheights(i)
Next
Return pages
End Function
''' <summary>
''' Check for more pages. This is called at the end of printing a page set.
''' If there's another page set to print, we return true.
''' </summary>
Private Function HasMorePages() As Boolean
currentpageset += 1
If currentpageset < pagesets.Count Then
'currentpageset--; // decrement back to a valid pageset number
Return True
' tell the caller we're through.
Else
Return False
End If
End Function
''' <summary>
''' Set values at start of print run
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub printDoc_BeginPrint(ByVal sender As Object, ByVal e As PrintEventArgs)
' reset counters since we'll go through this twice if we print from preview
currentpageset = 0
lastrowprinted = -1
CurrentPage = 0
End Sub
''' <summary>
''' PrintPage event handler. This routine prints one page. It will
''' skip non-printable pages if the user selected the "some pages" option
''' on the print dialog.
''' </summary>
''' <param name="sender">default object from windows</param>
''' <param name="e">Event info from Windows about the printing</param>
Private Sub printDoc_PrintPage(ByVal sender As Object, ByVal e As PrintPageEventArgs)
' flag for handling printing some pages rather than all
Dim printthispage As Boolean = False
' current printing position within one page
Dim printpos As Single = pagesets(currentpageset).margins.Top
' increment page number & check page range
CurrentPage += 1
If (CurrentPage >= fromPage) AndAlso (CurrentPage <= toPage) Then
printthispage = True
End If
' calculate the static vertical space available - this is where we stop printing rows
Dim staticheight As Single = pageHeight - footerHeight - pagesets(currentpageset).margins.Bottom
' holder for one-row height lookahead to see if the row will fit on the page
Dim nextrowheight As Single
'-----------------------------------------------------------------
' scan down heights until we're off this (non-printing) page
'-----------------------------------------------------------------
While Not printthispage
' calculate and increment over the page we're not printing
printpos = pagesets(currentpageset).margins.Top + headerHeight
' are we done with this page?
Dim pagecomplete As Boolean = False
' do one row look-ahead to see if we have room on the page
If lastrowprinted < rowheights.Count Then nextrowheight = rowheights(lastrowprinted + 1) Else nextrowheight = 0
While Not pagecomplete
' this page is complete if we run out of data
If lastrowprinted >= rowstoprint.Count - 1 Then
pagecomplete = True
' ... or off the bottom of the page
ElseIf (printpos + nextrowheight) >= staticheight Then
pagecomplete = True
Else
' not done yet so consume space
lastrowprinted += 1
printpos += rowheights(lastrowprinted)
If lastrowprinted + 1 < rowheights.Count Then nextrowheight = rowheights(lastrowprinted + 1) Else nextrowheight = 0
End If
End While
' skip to the next page & see if it's in the print range
CurrentPage += 1
If (CurrentPage >= fromPage) AndAlso (CurrentPage <= toPage) Then
printthispage = True
End If
' bottom check~ out of data or out of pages
If (lastrowprinted >= rowstoprint.Count - 1) OrElse (CurrentPage > toPage) Then
' reset for next pageset or tell the caller we're complete
e.HasMorePages = HasMorePages()
' reset counters since we'll go through this twice if we print from preview
lastrowprinted = -1
CurrentPage = 0
Exit Sub
End If
End While
'-----------------------------------------------------------------
' print headers
'-----------------------------------------------------------------
' reset printpos as it may have changed during the 'skip pages' routine just above.
printpos = pagesets(currentpageset).margins.Top
' print page number if user selected it
If pagenumberontop Then
' if we have a page number to print
If pageno Then
Dim pagenumber As String = m_pagetext + CurrentPage.ToString(CultureInfo.CurrentCulture)
If m_showtotalpagenumber Then
pagenumber += m_pageseparator + totalpages.ToString()
End If
If 1 < pagesets.Count Then
pagenumber += m_parttext + (currentpageset + 1).ToString(CultureInfo.CurrentCulture)
End If
' ... then print it
printsection(e.Graphics, printpos, pagenumber, pagenofont, pagenocolor, m_pagenumberformat, _
overridepagenumberformat, pagesets(currentpageset).margins)
' if the page number is not on a separate line, don't "use up" it's vertical space
If Not m_pagenumberonseparateline Then
printpos -= pagenumberHeight
End If
End If
End If
' print title if provided
If Not [String].IsNullOrEmpty(m_title) Then
printsection(e.Graphics, printpos, m_title, m_titlefont, m_titlecolor, m_titleformat, _
overridetitleformat, pagesets(currentpageset).margins)
End If
' print subtitle if provided
If Not [String].IsNullOrEmpty(m_subtitle) Then
printsection(e.Graphics, printpos, m_subtitle, m_subtitlefont, m_subtitlecolor, m_subtitleformat, _
overridesubtitleformat, pagesets(currentpageset).margins)
End If
' print column headers
printcolumnheaders(e.Graphics, printpos, pagesets(currentpageset))
'-----------------------------------------------------------------
' print rows until the page is complete
'-----------------------------------------------------------------
' do one row look-ahead to see if we have room on the page
If lastrowprinted < rowheights.Count Then nextrowheight = rowheights(lastrowprinted + 1) Else nextrowheight = 0
While (printpos + nextrowheight) < staticheight
lastrowprinted += 1
printrow(e.Graphics, printpos, DirectCast((rowstoprint(lastrowprinted)), DataGridViewRow), pagesets(currentpageset))
' bottom check, we're really really done when there's no more data
If lastrowprinted >= rowstoprint.Count - 1 Then
' print a footer for this page
printfooter(e.Graphics, printpos, pagesets(currentpageset).margins)
' check on more page sets or set no more pages flag
e.HasMorePages = HasMorePages()
' reset counters since we'll go through this twice if we print from preview
lastrowprinted = -1
CurrentPage = 0
Exit Sub
' return
Else
If lastrowprinted < rowheights.Count Then
nextrowheight = rowheights(lastrowprinted + 1)
Else
nextrowheight = 0
End If
End If
End While
'-----------------------------------------------------------------
' print footer
'-----------------------------------------------------------------
printfooter(e.Graphics, printpos, pagesets(currentpageset).margins)
'-----------------------------------------------------------------
' bottom check, see if this is the last page to print
'-----------------------------------------------------------------
If CurrentPage >= toPage Then
' reset for next pageset or tell the caller we're complete
e.HasMorePages = HasMorePages()
' reset counters since we'll go through this twice if we print from preview
lastrowprinted = -1
CurrentPage = 0
Else
' we're not done yet
e.HasMorePages = True
End If
Exit Sub
End Sub
Private Sub printsection(ByVal g As Graphics, ByRef pos As Single, ByVal text As String, ByVal font As Font, ByVal color As Color, ByVal format As StringFormat, _
ByVal useroverride As Boolean, ByVal margins As Margins)
' measure string
Dim printsize As SizeF = g.MeasureString(text, font, printWidth, format)
' build area to print within
Dim printarea As New RectangleF(CSng(margins.Left), pos, CSng(printWidth), printsize.Height)
' do the actual print
g.DrawString(text, font, New SolidBrush(color), printarea, format)
' track "used" vertical space
pos += printsize.Height
End Sub
''' <summary>
''' Print the footer. This handles the footer spacing, and printing the page number
''' at the bottom of the page (if the page number is not in the header).
''' </summary>
''' <param name="g">Graphic context to print in</param>
''' <param name="pos">Track vertical space used; 'y' location</param>
''' <param name="margins">The table's print margins</param>
Private Sub printfooter(ByVal g As Graphics, ByRef pos As Single, ByVal margins As Margins)
' print last footer. Note: need to force printpos to the bottom of the page
' as we may have run out of data anywhere on the page
pos = pageHeight - footerHeight - margins.Bottom
' - margins.Top
' add spacing
pos += m_footerspacing
' print the footer
printsection(g, pos, m_footer, m_footerfont, m_footercolor, m_footerformat, _
overridefooterformat, margins)
' print the page number if it's on the bottom.
If Not pagenumberontop Then
If pageno Then
Dim pagenumber As String = m_pagetext + CurrentPage.ToString(CultureInfo.CurrentCulture)
If m_showtotalpagenumber Then
pagenumber += m_pageseparator + totalpages.ToString()
End If
If 1 < pagesets.Count Then
pagenumber += m_parttext + (currentpageset + 1).ToString(CultureInfo.CurrentCulture)
End If
' if the pageno is not on a separate line, push the print location up by its height.
If Not m_pagenumberonseparateline Then
pos = pos - pagenumberHeight
End If
' print the page number
printsection(g, pos, pagenumber, pagenofont, pagenocolor, m_pagenumberformat, _
overridepagenumberformat, margins)
End If
End If
End Sub
''' <summary>
''' Print the column headers. Most printing format info is retrieved from the
''' source DataGridView.
''' </summary>
''' <param name="g">Graphics Context to print within</param>
''' <param name="pos">Track vertical space used; 'y' location</param>
''' <param name="pageset">Current pageset - defines columns and margins</param>
Private Sub printcolumnheaders(ByVal g As Graphics, ByRef pos As Single, ByVal pageset As PageDef)
' track printing location accross the page. start position is hard left,
' adjusted for the row headers. Note rowheaderwidth is 0 if row headers are not printed
Dim xcoord As Single = pageset.margins.Left + rowheaderwidth
' set the pen for drawing the grid lines
Dim lines As New Pen(dgv.GridColor, 1)
'-----------------------------------------------------------------
' Print the column headers
'-----------------------------------------------------------------
Dim col As DataGridViewColumn
For i As Integer = 0 To pageset.colstoprint.Count - 1
col = DirectCast(pageset.colstoprint(i), DataGridViewColumn)
' calc cell width, account for columns larger than the print area!
Dim cellwidth As Single
If pageset.colwidths(i) > printWidth - rowheaderwidth Then
cellwidth = printWidth - rowheaderwidth
Else
cellwidth = pageset.colwidths(i)
End If
' get column style
Dim style As DataGridViewCellStyle = col.HeaderCell.InheritedStyle
' set print area for this individual cell, account for cells larger
' than the print area!
Dim cellprintarea As New RectangleF(xcoord, pos, cellwidth, colheaderheight)
' print column header background
g.FillRectangle(New SolidBrush(style.BackColor), cellprintarea)
' draw the text
g.DrawString(col.HeaderText, style.Font, New SolidBrush(style.ForeColor), cellprintarea, headercellformat)
' draw the borders - default to the dgv's border setting; account for
' columns larger than the print width
If dgv.ColumnHeadersBorderStyle <> DataGridViewHeaderBorderStyle.None Then
g.DrawRectangle(lines, xcoord, pos, cellwidth, colheaderheight)
End If
xcoord += pageset.colwidths(i)
Next
' all done, consume "used" vertical space, including space for border lines
If dgv.ColumnHeadersBorderStyle <> DataGridViewHeaderBorderStyle.None Then
pos += colheaderheight + lines.Width
Else
pos += colheaderheight
End If
End Sub
''' <summary>
''' Print one row of the DataGridView. Most printing format info is retrieved
''' from the DataGridView.
''' </summary>
''' <param name="g">Graphics Context to print within</param>
''' <param name="pos">Track vertical space used; 'y' location</param>
''' <param name="row">The row that will be printed</param>
''' <param name="pageset">Current Pageset - defines columns and margins</param>
Private Sub printrow(ByVal g As Graphics, ByRef pos As Single, ByVal row As DataGridViewRow, ByVal pageset As PageDef)
' track printing location accross the page
Dim xcoord As Single = pageset.margins.Left
' set the pen for drawing the grid lines
Dim lines As New Pen(dgv.GridColor, 1)
'-----------------------------------------------------------------
' Print Row background
'-----------------------------------------------------------------
' get current row style, start with header style
Dim rowstyle As DataGridViewCellStyle = row.InheritedStyle
' calc row width, account for columns larger than the print area!
Dim rowwidth As Single
If pageset.coltotalwidth > printWidth Then
rowwidth = printWidth
Else
rowwidth = pageset.coltotalwidth
End If
' define print rectangle
Dim printarea As New RectangleF(xcoord, pos, rowwidth, rowheights(lastrowprinted))
' fill in the row background as the default color
g.FillRectangle(New SolidBrush(rowstyle.BackColor), printarea)
'-----------------------------------------------------------------
' Print the Row Headers, if they are visible
'-----------------------------------------------------------------
If dgv.RowHeadersVisible Then
' get current row style, start with header style
Dim headerstyle As DataGridViewCellStyle = row.HeaderCell.InheritedStyle
' set print area for this individual cell
Dim headercellprintarea As New RectangleF(xcoord, pos, rowheaderwidth, rowheights(lastrowprinted))
' fill in the row header background
g.FillRectangle(New SolidBrush(headerstyle.BackColor), headercellprintarea)
' draw the text for the row header cell
g.DrawString(row.HeaderCell.EditedFormattedValue.ToString(), headerstyle.Font, New SolidBrush(headerstyle.ForeColor), headercellprintarea, headercellformat)
' draw the borders - default to the dgv's border setting
If dgv.RowHeadersBorderStyle <> DataGridViewHeaderBorderStyle.None Then
g.DrawRectangle(lines, xcoord, pos, rowheaderwidth, rowheights(lastrowprinted))
End If
' track horizontal space used
xcoord += rowheaderwidth
End If
'-----------------------------------------------------------------
' Print the row: write and draw each cell
'-----------------------------------------------------------------
Dim col As DataGridViewColumn
For i As Integer = 0 To pageset.colstoprint.Count - 1
' access the column being printed
col = DirectCast(pageset.colstoprint(i), DataGridViewColumn)
' access the data to be printed - weird bug: had to move this up here since
' doing this access actually changes the cell's style. ???
Dim datastr As String = row.Cells(col.Index).EditedFormattedValue.ToString()
' calc cell width, account for columns larger than the print area!
Dim cellwidth As Single
If pageset.colwidths(i) > printWidth - rowheaderwidth Then cellwidth = printWidth - rowheaderwidth Else cellwidth = pageset.colwidths(i)
' get DGV column style and see if we have an override for this column
Dim finalformat As StringFormat = Nothing
Dim cellfont As Font = Nothing
Dim colstyle As DataGridViewCellStyle = Nothing
If ColumnStyles.ContainsKey(col.Name) Then
colstyle = colstyles(col.Name)
' set string format
buildstringformat(finalformat, colstyle, cellformat.Alignment, cellformat.LineAlignment, cellformat.FormatFlags, cellformat.Trimming)
cellfont = colstyle.Font
ElseIf (col.HasDefaultCellStyle) OrElse (row.Cells(col.Index).HasStyle) Then
colstyle = row.Cells(col.Index).InheritedStyle
' set string format
buildstringformat(finalformat, colstyle, cellformat.Alignment, cellformat.LineAlignment, cellformat.FormatFlags, cellformat.Trimming)
cellfont = colstyle.Font
Else
finalformat = cellformat
' inherited style == default style (mostly) if no style was ever set.
colstyle = row.Cells(col.Index).InheritedStyle
End If
' set print area for this individual cell
Dim cellprintarea As New RectangleF(xcoord, pos, cellwidth, rowheights(lastrowprinted))
' fill in the cell background - using the selected style
g.FillRectangle(New SolidBrush(colstyle.BackColor), cellprintarea)
' draw the text for the cell at the row / col intersection
g.DrawString(datastr, colstyle.Font, New SolidBrush(colstyle.ForeColor), cellprintarea, finalformat)
' draw the borders - default to the dgv's border setting
If dgv.CellBorderStyle <> DataGridViewCellBorderStyle.None Then
g.DrawRectangle(lines, xcoord, pos, cellwidth, rowheights(lastrowprinted))
End If
' track horizontal space used
xcoord += pageset.colwidths(i)
Next
'-----------------------------------------------------------------
' All done with this row, consume "used" vertical space
'-----------------------------------------------------------------
pos += rowheights(lastrowprinted)
End Sub
End Class
End Namespace
|