Click here to Skip to main content
15,911,360 members
Articles / Programming Languages / Visual Basic

Another DataGridView Printer

Rate me:
Please Sign up or sign in to vote.
4.86/5 (186 votes)
6 Feb 2014CPOL5 min read 4.3M   55.8K   412   984
DataGridView printing encapsulated in a stand-alone object. Very easy to use! Updated to allow printing columns wider than one page.

Introduction

I went looking for a class to do printing from a DataGridView, and none of them did all that I was looking for. I needed to print all pages, some pages, or the current selection; and I needed to not have objects, controls, or code from the printer object sprinkled through the rest of my code - i.e., it needed to be completely self-contained. Nothing I found met all those requirements, so I ended up writing my own.

Using the Code

To use the DGVPrinter class, you have two options. First, you can simply add the DGVPrinter.cs source file to your project, or second you can place the DLL in your "Bin" directory and add a reference to the DGVPrinter.dll to your project's references. In either case, to use the DGVPrinter, you will only need to add a "using DGVPrinter" to your code file, and create an instance of the object.

C#
//

// The using block statement

//

using DGVPrinterHelper;
//

// The using block statement

//

imports DGVPrinterHelper;

For example, if you wanted to print a DataGridView when your user clicks a Print button on the toolbar, your code might look something like this:

C#
//

// Printing the DataGridView Control

// in response to a toolbar button press

//

private void printToolStripButton_Click(object sender, EventArgs e)

{

    DGVPrinter printer = new DGVPrinter();

    printer.Title = "DataGridView Report";

    printer.SubTitle = "An Easy to Use DataGridView Printing Object";

    printer.SubTitleFormatFlags = StringFormatFlags.LineLimit | 

                                  StringFormatFlags.NoClip;

    printer.PageNumbers = true;

    printer.PageNumberInHeader = false;

    printer.PorportionalColumns = true;

    printer.HeaderCellAlignment = StringAlignment.Near;

    printer.Footer = "Your Company Name Here";

    printer.FooterSpacing = 15;



    printer.PrintDataGridView(datagridviewControl);

}
//

// Printing the DataGridView Control

// in response to a toolbar button press

//

Public Class Form1 

    Private Sub btnPrintGridview_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles btnPrintGridView.Click

        Dim Printer = New DGVPrinter

        Printer.Title = "DataGridView Report"

        Printer.SubTitle = "An Easy to Use DataGridView Printing Object"

        Printer.SubTitleFormatFlags = StringFormatFlags.LineLimit Or _

                    StringFormatFlags.NoClip

        Printer.PageNumbers = True

        Printer.PageNumberInHeader = False

        Printer.PorportionalColumns = True

        Printer.HeaderCellAlignment = StringAlignment.Near

        Printer.Footer = "Your Company Name Here"

        Printer.FooterSpacing = 15

        Printer.PrintDataGridView(Me.DataGridView1)

    End Sub

The basic interface used here provides a neat, one-stop-shop for printing a DataGridView. But, what if you want to have more control over the printing process? Say you'd like to save your users' print preferences or provide a default printer? To help you with this, DGVPrinter provides a more complex interface. Here's an example where the calling program provides overrides to the PrinterSettings and the DefaultPageSettings:

C#
//

// Printing the DataGridView Control

// in response to a toolbar button press – 

// the myprintsettings and mypagesettings objects are objects used by the local

// program to save printer and page settings

//

private void printToolStripButton_Click(object sender, EventArgs e)

{

    DGVPrinter printer = new DGVPrinter();

    printer.Title = "DataGridView Report";

    printer.SubTitle = "An Easy to Use DataGridView Printing Object";

    printer.SubTitleFormatFlags = StringFormatFlags.LineLimit | 

        StringFormatFlags.NoClip;

    printer.PageNumbers = true;

    printer.PageNumberInHeader = false;

    printer.PorportionalColumns = true;

    printer.HeaderCellAlignment = StringAlignment.Near;

    printer.Footer = "Your Company Name Here";

    printer.FooterSpacing = 15;



    // use saved settings

    if (null != myprintsettings) 

        printer.PrintDocument.PrinterSettings = myprintsettings;

    if (null != mypagesettings)

        printer.PrintDocument.DefaultPageSettings = mypagesettings;



    if (DialogResult.OK == printer.DisplayPrintDialog())  // replace DisplayPrintDialog() 

                           // with your own print dialog

    {

        // save users' settings 

        myprintsettings = printer.PrinterSettings;

        mypagesettings = printer.PageSettings;



        // print without displaying the printdialog

        printer.PrintNoDisplay(datagridviewControl);

    }

}

DGVPrinter's various settings provide good control of all aspects of printing on the page. You can set the Title and Subtitles, add a footer, and control whether the page number prints in the header or footer. DGVPrinter supports Right-to-Left printing for non-Western languages and includes a drawing override for situations where a cell or column has onPaint overridden in the source DataGridView control. While the default styles for the printed DataGridView are taken from the source DataGridView control, DGVPrinter also provides many attributes that allow you to control the styling of almost every aspect of the printout.

History

  • Version 1.0 - Initial publication
  • Version 1.1 - Added footer handling, and allows the page number to print in the header or footer, and if it should print on the same line as the header or footer
  • Version 1.2 - Finally (I believe!), fixed the string/column alignment problems. Also prints cell background colors properly, respecting the alternating rows style
  • Version 1.3 - Added support for printing columns that contain images
  • Version 1.4 - Added support for printing directly to a provided Graphics object
  • Version 2.0 - Added support for printing images on the page
  • Version 3.0 - Breaking changes! Please read!
    1. Added support for cells/rows that span more than one page of depth. If a cell would run off the bottom of the page, the "KeepRowsTogether" property determines if a partial row is printed or a new page is started.
    2. Added support for Setting the styles for Row and Column Headers. The properties for setting Header cell styles changed names, and the return type of "PrintColumnHeaders" changed. This can cause your program to not compile/run!
    3. Added a default value so row headers will show up if they are supposed to be "visible"
    4. Added title and subtitle spacers. These will help give you control of the whitespace below the Title and Subtitle.
    5. Compiled version for VB and other language support
  • Version 3.1 - Fix cell background color printing
  • Version 3.2 - Fixes for Embedded Print function
  • Version 3.3 - Unlikely but possible breaking change
    1. Add Delegate to allow "Owner Drawing" of cells, including row and column headers
    2. Add better support for cell size, data size or proportional scaling of columns. The identifier 'StringHeight' has been changed to 'DataHeight' since the size of an image is now properly accounted for. This may break your code if you depend on this feature.
    3. Bug Fixes
  • Version 3.4 - Add support for Alternating Rows when ColumnStyles are overridden
  • Version 3.5 - More fixes for Alternating Rows ColumnStyles
  • Version 3.6 - Fix for Imbedded Image drawing; images now draw at original pixel sizes without scaling
  • Version 3.7 - Fix for large text wrapping
  • Version 4.0 - Fixes and lots of new functionality!
    1. Bug Fixes
      1. Font resizing problem found by Member 8539779
      2. Rows that spanned multiple pages did not properly respect Right-to-Left Language setting
    2. New Features - Many thanks to everyone who suggested a new function or feature!!
      1. Set background colors/shading for Title, Subtitle and Footer
      2. Set border style and colors for Title, Subtitle and Footer
      3. Hide Columns - Specify a list of columns that will not be printed
      4. Fixed Columns - Specify a list of columns that will be printed on every page (good for rows spanning multiple pages). Respects Right-to-Left language setting
      5. Break on Value Change - Specify a column to monitor. When the value in a cell of this columns changes, a page break is inserted.
      6. Checkbox column now prints as an actual graphic checkbox.
  • Version 4.1 
    1. Greatly expanded Embedded Print process, now supports multiple pages and exposes the BeginPrint and PrintPage events.
    2. Added log/tracing facility
    3. Bug Fix - thanks and kudos to B. Marik for patience and help while I tracked this one down. 
  • Version 4.2
    1. Bug Fix - Print no longer throws at error. Thanks to Member 8757586 for finding it for me!
  • Version 4.3
    1. Bug Fix - Improved handling of checkboxes, added support for tristate checkbox.
    2. Bug Fix - Column headers would not print if the first column was hidden
  • Version 4.4 (Thanks to Derek for finding and helping me find and fix these!)
    1. Bug Fix - handle cells with large amounts of data.
    2. Bug Fix - Footer printing over the bottom row of text in certian situations.

Gratitude, kudos and acknowledgements to everyone who suggested a feature or function or found a bug. DGV Printer just wouldn't be the same without everyone's ideas and input!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: Too Many Columns? Pin
aureolin2-Dec-08 12:42
aureolin2-Dec-08 12:42 
QuestionRe: Too Many Columns? Pin
JohnRou4-Dec-08 10:08
JohnRou4-Dec-08 10:08 
AnswerRe: Too Many Columns? Pin
aureolin4-Dec-08 11:46
aureolin4-Dec-08 11:46 
QuestionRe: Too Many Columns? Pin
JohnRou5-Dec-08 4:56
JohnRou5-Dec-08 4:56 
AnswerRe: Too Many Columns? Pin
aureolin7-Dec-08 9:35
aureolin7-Dec-08 9:35 
AnswerRe: Too Many Columns? - Update to VB code Pin
JohnRou10-Dec-08 8:20
JohnRou10-Dec-08 8:20 
QuestionVB.Net ? Pin
Toumar1-Dec-08 22:28
Toumar1-Dec-08 22:28 
AnswerRe: VB.Net ? Pin
Toumar1-Dec-08 23:06
Toumar1-Dec-08 23:06 
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

GeneralRe: VB.Net ? Pin
Toumar1-Dec-08 23:07
Toumar1-Dec-08 23:07 
AnswerPrinting Row Headers Pin
aureolin2-Dec-08 5:30
aureolin2-Dec-08 5:30 
GeneralRe: VB.Net ? Pin
Karl Angel10-Mar-09 2:08
Karl Angel10-Mar-09 2:08 
GeneralRe: VB.Net ? Pin
walkesh16-Dec-09 9:13
walkesh16-Dec-09 9:13 
QuestionMultiLine Headers? Pin
Joe Pool13-Nov-08 11:50
Joe Pool13-Nov-08 11:50 
AnswerRe: MultiLine Headers? Pin
aureolin13-Nov-08 12:24
aureolin13-Nov-08 12:24 
QuestionReplace Built-In Print Dialog? Pin
Joe Pool13-Nov-08 9:44
Joe Pool13-Nov-08 9:44 
Generalnever get the PrintPrieview Pin
Rafone8-Oct-08 19:42
Rafone8-Oct-08 19:42 
GeneralRe: never get the PrintPrieview Pin
aureolin9-Oct-08 9:32
aureolin9-Oct-08 9:32 
GeneralRe: never get the PrintPrieview Pin
Rafone9-Oct-08 12:10
Rafone9-Oct-08 12:10 
GeneralRe: never get the PrintPrieview Pin
aureolin9-Oct-08 12:26
aureolin9-Oct-08 12:26 
GeneralRe: never get the PrintPrieview Pin
Rafone9-Oct-08 14:06
Rafone9-Oct-08 14:06 
GeneralRe: never get the PrintPrieview Pin
aureolin9-Oct-08 14:11
aureolin9-Oct-08 14:11 
GeneralRe: never get the PrintPrieview Pin
Rafone9-Oct-08 15:26
Rafone9-Oct-08 15:26 
GeneralRe: never get the PrintPrieview Pin
Rafone9-Oct-08 15:44
Rafone9-Oct-08 15:44 
GeneralRe: never get the PrintPrieview Pin
aureolin9-Oct-08 16:33
aureolin9-Oct-08 16:33 
GeneralRe: never get the PrintPrieview Pin
Rafone12-Oct-08 13:07
Rafone12-Oct-08 13:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.