Introduction
The DataGridView
control in .NET 2.0 is an amazing data representation control, and contains many advanced features that we could benefit from. The only thing that is not supported by this control is the printing feature. I searched the web for such a feature, but did not find anything really good, and most of the printing classes that I found were for printing a DataTable
object or for printing the traditional DataGrid
control. Therefore, I decided to create my own code for this feature and share it with others.
The Class Features
- The print style is almost the same as the style of the
DataGridView
control:
- the same font style for the header and other rows
- the same foreground and background styles for the header and other rows
- the same alternating background style for the rows
- special font for certain rows will be considered
- special foreground and background styles for certain rows will be considered
- the same alignment for the columns
- Supports multiple pages
- The width of each column to be printed is calculated to fit all the cells (including the header cell)
- The title at the top of the page can be specified
- The title font and color could be specified
- The title and the header row are repeated in each page
- The report could be top-centered (considering the top margin of the page) on the page or be aligned to the top-left margin
- The printing process ignores any invisible rows or columns (assuming that the user does not want them to appear)
- If the
DataGridView
width is greater than the page width, then the columns with x coordinate greater than the page width will be printed into another page. This ensures that all columns will be printed (Thanks to Stephen Long) - Support page numbering
- The printing process uses
Graphics.MeasureString
to calculate the height and width for a certain text with a specified font. This ensures the preciseness of the printing. - The class supports Right-to-Left fonts
The Class Constructor
public DataGridViewPrinter(DataGridView aDataGridView, PrintDocument aPrintDocument,
bool CenterOnPage, bool WithTitle, string aTitleText, Font aTitleFont,
Color aTitleColor, bool WithPaging)
aDataGridView
: The DataGridView
control which will be printed aPrintDocument
: The PrintDocument
to be used for printing CenterOnPage
: Determines if the report will be printed in the top-center of the page WithTitle
: Determines if the page contains a title text aTitleText
: The title text to be printed in each page (if WithTitle
is set to true
) aTitleFont
: The font to be used with the title text (if WithTitle
is set to true
) aTitleColor
: The color to be used with the title text (if WithTitle
is set to true
) WithPaging
: Determines if the page number will be printed
How to Use the Class
The project should have the following global objects:
DataGridView MyDataGridView;
PrintDocument MyPrintDocument;
DataGridViewPrinter MyDataGridViewPrinter;
Then, use the following code:
private bool SetupThePrinting()
{
PrintDialog MyPrintDialog = new PrintDialog();
MyPrintDialog.AllowCurrentPage = false;
MyPrintDialog.AllowPrintToFile = false;
MyPrintDialog.AllowSelection = false;
MyPrintDialog.AllowSomePages = false;
MyPrintDialog.PrintToFile = false;
MyPrintDialog.ShowHelp = false;
MyPrintDialog.ShowNetwork = false;
if (MyPrintDialog.ShowDialog() != DialogResult.OK)
return false;
MyPrintDocument.DocumentName = "Customers Report";
MyPrintDocument.PrinterSettings =
MyPrintDialog.PrinterSettings;
MyPrintDocument.DefaultPageSettings =
MyPrintDialog.PrinterSettings.DefaultPageSettings;
MyPrintDocument.DefaultPageSettings.Margins =
new Margins(40, 40, 40, 40);
if (MessageBox.Show("Do you want the report to be centered on the page",
"InvoiceManager - Center on Page", MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
MyDataGridViewPrinter = new DataGridViewPrinter(MyDataGridView,
MyPrintDocument, true, true, "Customers", new Font("Tahoma", 18,
FontStyle.Bold, GraphicsUnit.Point), Color.Black, true);
else
MyDataGridViewPrinter = new DataGridViewPrinter(MyDataGridView,
MyPrintDocument, false, true, "Customers", new Font("Tahoma", 18,
FontStyle.Bold, GraphicsUnit.Point), Color.Black, true);
return true;
}
private void btnPrint_Click(object sender, EventArgs e)
{
if (SetupThePrinting())
MyPrintDocument.Print();
}
private void MyPrintDocument_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
bool more = MyDataGridViewPrinter.DrawDataGridView(e.Graphics);
if (more == true)
e.HasMorePages = true;
}
private void btnPrintPreview_Click(object sender, EventArgs e)
{
if (SetupThePrinting())
{
PrintPreviewDialog MyPrintPreviewDialog = new PrintPreviewDialog();
MyPrintPreviewDialog.Document = MyPrintDocument;
MyPrintPreviewDialog.ShowDialog();
}
}
Refer to the demo project that contains the DataGridViewPrinter
class (.cs file) and a simple example showing how to use it.
The DataGridViewPrinter Class Source Code
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.Data;
using System.Windows.Forms;
class DataGridViewPrinter
{
private DataGridView TheDataGridView;
private PrintDocument ThePrintDocument;
private bool IsCenterOnPage;
private bool IsWithTitle;
private string TheTitleText;
private Font TheTitleFont;
private Color TheTitleColor;
private bool IsWithPaging;
static int CurrentRow;
static int PageNumber;
private int PageWidth;
private int PageHeight;
private int LeftMargin;
private int TopMargin;
private int RightMargin;
private int BottomMargin;
private float CurrentY;
private float RowHeaderHeight;
private List<float> RowsHeight;
private List<float> ColumnsWidth;
private float TheDataGridViewWidth;
private List<int[]> mColumnPoints;
private List<float> mColumnPointsWidth;
private int mColumnPoint;
public DataGridViewPrinter(DataGridView aDataGridView,
PrintDocument aPrintDocument,
bool CenterOnPage, bool WithTitle,
string aTitleText, Font aTitleFont,
Color aTitleColor, bool WithPaging)
{
TheDataGridView = aDataGridView;
ThePrintDocument = aPrintDocument;
IsCenterOnPage = CenterOnPage;
IsWithTitle = WithTitle;
TheTitleText = aTitleText;
TheTitleFont = aTitleFont;
TheTitleColor = aTitleColor;
IsWithPaging = WithPaging;
PageNumber = 0;
RowsHeight = new List<float>();
ColumnsWidth = new List<float>();
mColumnPoints = new List<int[]>();
mColumnPointsWidth = new List<float>();
if (!ThePrintDocument.DefaultPageSettings.Landscape)
{
PageWidth =
ThePrintDocument.DefaultPageSettings.PaperSize.Width;
PageHeight =
ThePrintDocument.DefaultPageSettings.PaperSize.Height;
}
else
{
PageHeight =
ThePrintDocument.DefaultPageSettings.PaperSize.Width;
PageWidth =
ThePrintDocument.DefaultPageSettings.PaperSize.Height;
}
LeftMargin = ThePrintDocument.DefaultPageSettings.Margins.Left;
TopMargin = ThePrintDocument.DefaultPageSettings.Margins.Top;
RightMargin = ThePrintDocument.DefaultPageSettings.Margins.Right;
BottomMargin = ThePrintDocument.DefaultPageSettings.Margins.Bottom;
CurrentRow = 0;
}
private void Calculate(Graphics g)
{
if (PageNumber == 0)
{
SizeF tmpSize = new SizeF();
Font tmpFont;
float tmpWidth;
TheDataGridViewWidth = 0;
for (int i = 0; i < TheDataGridView.Columns.Count; i++)
{
tmpFont = TheDataGridView.ColumnHeadersDefaultCellStyle.Font;
if (tmpFont == null)
tmpFont = TheDataGridView.DefaultCellStyle.Font;
tmpSize = g.MeasureString(
TheDataGridView.Columns[i].HeaderText,
tmpFont);
tmpWidth = tmpSize.Width;
RowHeaderHeight = tmpSize.Height;
for (int j = 0; j < TheDataGridView.Rows.Count; j++)
{
tmpFont = TheDataGridView.Rows[j].DefaultCellStyle.Font;
if (tmpFont == null)
tmpFont = TheDataGridView.DefaultCellStyle.Font;
tmpSize = g.MeasureString("Anything", tmpFont);
RowsHeight.Add(tmpSize.Height);
tmpSize =
g.MeasureString(
TheDataGridView.Rows[j].Cells[i].
EditedFormattedValue.ToString(),
tmpFont);
if (tmpSize.Width > tmpWidth)
tmpWidth = tmpSize.Width;
}
if (TheDataGridView.Columns[i].Visible)
TheDataGridViewWidth += tmpWidth;
ColumnsWidth.Add(tmpWidth);
}
int k;
int mStartPoint = 0;
for (k = 0; k < TheDataGridView.Columns.Count; k++)
if (TheDataGridView.Columns[k].Visible)
{
mStartPoint = k;
break;
}
int mEndPoint = TheDataGridView.Columns.Count;
for (k = TheDataGridView.Columns.Count - 1; k >= 0; k--)
if (TheDataGridView.Columns[k].Visible)
{
mEndPoint = k + 1;
break;
}
float mTempWidth = TheDataGridViewWidth;
float mTempPrintArea = (float)PageWidth - (float)LeftMargin -
(float)RightMargin;
if (TheDataGridViewWidth > mTempPrintArea)
{
mTempWidth = 0.0F;
for (k = 0; k < TheDataGridView.Columns.Count; k++)
{
if (TheDataGridView.Columns[k].Visible)
{
mTempWidth += ColumnsWidth[k];
if (mTempWidth > mTempPrintArea)
{
mTempWidth -= ColumnsWidth[k];
mColumnPoints.Add(new int[] { mStartPoint, mEndPoint });
mColumnPointsWidth.Add(mTempWidth);
mStartPoint = k;
mTempWidth = ColumnsWidth[k];
}
}
mEndPoint = k + 1;
}
}
mColumnPoints.Add(new int[] { mStartPoint, mEndPoint });
mColumnPointsWidth.Add(mTempWidth);
mColumnPoint = 0;
}
}
private void DrawHeader(Graphics g)
{
CurrentY = (float)TopMargin;
if (IsWithPaging)
{
PageNumber++;
string PageString = "Page " + PageNumber.ToString();
StringFormat PageStringFormat = new StringFormat();
PageStringFormat.Trimming = StringTrimming.Word;
PageStringFormat.FormatFlags = StringFormatFlags.NoWrap |
StringFormatFlags.LineLimit | StringFormatFlags.NoClip;
PageStringFormat.Alignment = StringAlignment.Far;
Font PageStringFont = new Font("Tahoma", 8, FontStyle.Regular,
GraphicsUnit.Point);
RectangleF PageStringRectangle =
new RectangleF((float)LeftMargin, CurrentY,
(float)PageWidth - (float)RightMargin - (float)LeftMargin,
g.MeasureString(PageString, PageStringFont).Height);
g.DrawString(PageString, PageStringFont,
new SolidBrush(Color.Black),
PageStringRectangle, PageStringFormat);
CurrentY += g.MeasureString(PageString,
PageStringFont).Height;
}
if (IsWithTitle)
{
StringFormat TitleFormat = new StringFormat();
TitleFormat.Trimming = StringTrimming.Word;
TitleFormat.FormatFlags = StringFormatFlags.NoWrap |
StringFormatFlags.LineLimit | StringFormatFlags.NoClip;
if (IsCenterOnPage)
TitleFormat.Alignment = StringAlignment.Center;
else
TitleFormat.Alignment = StringAlignment.Near;
RectangleF TitleRectangle =
new RectangleF((float)LeftMargin, CurrentY,
(float)PageWidth - (float)RightMargin - (float)LeftMargin,
g.MeasureString(TheTitleText, TheTitleFont).Height);
g.DrawString(TheTitleText, TheTitleFont,
new SolidBrush(TheTitleColor),
TitleRectangle, TitleFormat);
CurrentY += g.MeasureString(TheTitleText, TheTitleFont).Height;
}
float CurrentX = (float)LeftMargin;
if (IsCenterOnPage)
CurrentX += (((float)PageWidth - (float)RightMargin -
(float)LeftMargin) - mColumnPointsWidth[mColumnPoint]) / 2.0F;
Color HeaderForeColor =
TheDataGridView.ColumnHeadersDefaultCellStyle.ForeColor;
if (HeaderForeColor.IsEmpty)
HeaderForeColor = TheDataGridView.DefaultCellStyle.ForeColor;
SolidBrush HeaderForeBrush = new SolidBrush(HeaderForeColor);
Color HeaderBackColor =
TheDataGridView.ColumnHeadersDefaultCellStyle.BackColor;
if (HeaderBackColor.IsEmpty)
HeaderBackColor = TheDataGridView.DefaultCellStyle.BackColor;
SolidBrush HeaderBackBrush = new SolidBrush(HeaderBackColor);
Pen TheLinePen = new Pen(TheDataGridView.GridColor, 1);
Font HeaderFont = TheDataGridView.ColumnHeadersDefaultCellStyle.Font;
if (HeaderFont == null)
HeaderFont = TheDataGridView.DefaultCellStyle.Font;
RectangleF HeaderBounds = new RectangleF(CurrentX, CurrentY,
mColumnPointsWidth[mColumnPoint], RowHeaderHeight);
g.FillRectangle(HeaderBackBrush, HeaderBounds);
StringFormat CellFormat = new StringFormat();
CellFormat.Trimming = StringTrimming.Word;
CellFormat.FormatFlags = StringFormatFlags.NoWrap |
StringFormatFlags.LineLimit | StringFormatFlags.NoClip;
RectangleF CellBounds;
float ColumnWidth;
for (int i = (int)mColumnPoints[mColumnPoint].GetValue(0);
i < (int)mColumnPoints[mColumnPoint].GetValue(1); i++)
{
if (!TheDataGridView.Columns[i].Visible) continue;
ColumnWidth = ColumnsWidth[i];
if (TheDataGridView.ColumnHeadersDefaultCellStyle.
Alignment.ToString().Contains("Right"))
CellFormat.Alignment = StringAlignment.Far;
else if (TheDataGridView.ColumnHeadersDefaultCellStyle.
Alignment.ToString().Contains("Center"))
CellFormat.Alignment = StringAlignment.Center;
else
CellFormat.Alignment = StringAlignment.Near;
CellBounds = new RectangleF(CurrentX, CurrentY,
ColumnWidth, RowHeaderHeight);
g.DrawString(TheDataGridView.Columns[i].HeaderText,
HeaderFont, HeaderForeBrush,
CellBounds, CellFormat);
if (TheDataGridView.RowHeadersBorderStyle !=
DataGridViewHeaderBorderStyle.None)
g.DrawRectangle(TheLinePen, CurrentX, CurrentY, ColumnWidth,
RowHeaderHeight);
CurrentX += ColumnWidth;
}
CurrentY += RowHeaderHeight;
}
private bool DrawRows(Graphics g)
{
Pen TheLinePen = new Pen(TheDataGridView.GridColor, 1);
Font RowFont;
Color RowForeColor;
Color RowBackColor;
SolidBrush RowForeBrush;
SolidBrush RowBackBrush;
SolidBrush RowAlternatingBackBrush;
StringFormat CellFormat = new StringFormat();
CellFormat.Trimming = StringTrimming.Word;
CellFormat.FormatFlags = StringFormatFlags.NoWrap |
StringFormatFlags.LineLimit;
RectangleF RowBounds;
float CurrentX;
float ColumnWidth;
while (CurrentRow < TheDataGridView.Rows.Count)
{
if (TheDataGridView.Rows[CurrentRow].Visible)
{
RowFont = TheDataGridView.Rows[CurrentRow].DefaultCellStyle.Font;
if (RowFont == null)
RowFont = TheDataGridView.DefaultCellStyle.Font;
RowForeColor =
TheDataGridView.Rows[CurrentRow].DefaultCellStyle.ForeColor;
if (RowForeColor.IsEmpty)
RowForeColor = TheDataGridView.DefaultCellStyle.ForeColor;
RowForeBrush = new SolidBrush(RowForeColor);
RowBackColor =
TheDataGridView.Rows[CurrentRow].DefaultCellStyle.BackColor;
if (RowBackColor.IsEmpty)
{
RowBackBrush = new SolidBrush(
TheDataGridView.DefaultCellStyle.BackColor);
RowAlternatingBackBrush = new
SolidBrush(
TheDataGridView.AlternatingRowsDefaultCellStyle.BackColor);
}
else
{
RowBackBrush = new SolidBrush(RowBackColor);
RowAlternatingBackBrush = new SolidBrush(RowBackColor);
}
CurrentX = (float)LeftMargin;
if (IsCenterOnPage)
CurrentX += (((float)PageWidth - (float)RightMargin -
(float)LeftMargin) -
mColumnPointsWidth[mColumnPoint]) / 2.0F;
RowBounds = new RectangleF(CurrentX, CurrentY,
mColumnPointsWidth[mColumnPoint], RowsHeight[CurrentRow]);
if (CurrentRow % 2 == 0)
g.FillRectangle(RowBackBrush, RowBounds);
else
g.FillRectangle(RowAlternatingBackBrush, RowBounds);
for (int CurrentCell = (int)mColumnPoints[mColumnPoint].GetValue(0);
CurrentCell < (int)mColumnPoints[mColumnPoint].GetValue(1);
CurrentCell++)
{
if (!TheDataGridView.Columns[CurrentCell].Visible) continue;
if (TheDataGridView.Columns[CurrentCell].DefaultCellStyle.
Alignment.ToString().Contains("Right"))
CellFormat.Alignment = StringAlignment.Far;
else if (TheDataGridView.Columns[CurrentCell].DefaultCellStyle.
Alignment.ToString().Contains("Center"))
CellFormat.Alignment = StringAlignment.Center;
else
CellFormat.Alignment = StringAlignment.Near;
ColumnWidth = ColumnsWidth[CurrentCell];
RectangleF CellBounds = new RectangleF(CurrentX, CurrentY,
ColumnWidth, RowsHeight[CurrentRow]);
g.DrawString(
TheDataGridView.Rows[CurrentRow].Cells[CurrentCell].
EditedFormattedValue.ToString(), RowFont, RowForeBrush,
CellBounds, CellFormat);
if (TheDataGridView.CellBorderStyle !=
DataGridViewCellBorderStyle.None)
g.DrawRectangle(TheLinePen, CurrentX, CurrentY,
ColumnWidth, RowsHeight[CurrentRow]);
CurrentX += ColumnWidth;
}
CurrentY += RowsHeight[CurrentRow];
if ((int)CurrentY > (PageHeight - TopMargin - BottomMargin))
{
CurrentRow++;
return true;
}
}
CurrentRow++;
}
CurrentRow = 0;
mColumnPoint++;
if (mColumnPoint == mColumnPoints.Count)
{
mColumnPoint = 0;
return false;
}
else
return true;
}
public bool DrawDataGridView(Graphics g)
{
try
{
Calculate(g);
DrawHeader(g);
bool bContinue = DrawRows(g);
return bContinue;
}
catch (Exception ex)
{
MessageBox.Show("Operation failed: " + ex.Message.ToString(),
Application.ProductName + " - Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
return false;
}
}
}
Problems With the Class
- If a certain column's width is greater than the page width, then the excluded text will not be wrapped or printed in another page.
- The class does not support image cells.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.