DataGridView Printing by Selecting Columns and Rows






4.85/5 (99 votes)
An article on DataGrdView printing.
- Download demo project [C#] - 38.5 Kb
- Download source [C#] - 47.2 Kb
- Download demo project [VB.NET] - 42.9 Kb
- Download source [VB.NET] - 48.3 Kb
Introduction
Sometimes the user needs to print specific columns and rows (or all of them) in a DataGridView
. Some cases can be as below:
- There are too many rows in a
DataGridView
and there is no need to print all of them. - Sum of the column widths may be wider than the page width, and it is better to remove one or more columns while printing.
So a class named PrintDGV
was implemented, and can be used in any .NET2.0 application. I have done it both for the DataGrid and for the DataGridView
, in C# and VB.NET.
Description
The main section of the code is the PrintDGV
class and the PrintOptions
form.
In the PrintDGV
, we have:
- The
SelectedColumns
andAvailableColumns
lists to hold column names. - A
PrintDocument
object namedPrintDoc
(with theBeginPrint
andPrintPage
event handlers) - Four functions:
Print_DataGridView
: The main function that can be called from outside of the class.PrintDoc_BeginPrint
: Initializes some variables to begin printing.PrintDoc_PrintPage
: Performs the printing job.DrawFooter
: Writes the page number.
The code has the following features :
- Supports
TextBox
,Link
,Button
,ComboBox
,CheckBox
, andImage
columns. - Draws the columns to fit on the page width, or draws them as they are shown.
- Wraps the cell text if the cell width is smaller than its contents.
- Draws the page footer, date, time, and the title on the page.
Using the code
For using the code in an application, the PrintDGV
class and the PrintOptions
form must be added to it, and then the function Print_DataGridView
is called. In our example, the sample project has a Form
named MainForm
.
In the MainForm
, the DataGridView
is filled with 'Persons' table of the 'Persongs.mdb'.
In the PrintOption
form, the DataGridView
Columns, Fitting to page, and the Title can be selected by user.
The PrintDoc_PrintPage
event handler does the following tasks :
- Calculates the column widths.
- Prints the current page, row by row - loops through all rows in the
DataGridView
. - In the 'Print Selected Rows' mode, if the current row is not selected, then skips it.
- If it reaches the end of the page, then writes the page number and goes to the next page. If it doesn't reach the end of the page then:
- If it is in a new page, then draws the title, date-time, headers, and columns (checks to see if each column was selected by the user, if not, skips it).
- Draws the column contents for the
TextBox
,Link
,Button
,CheckBox
,ComboBox
, andImage
cells (checks to see if each column was selected by the user, if not, skips it). - Draws the borders.
- Calculates the 'Rows per Page' for the first page.
- Writes the page footer (page number).
private static void PrintDoc_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
int tmpWidth, i;
int tmpTop = e.MarginBounds.Top;
int tmpLeft = e.MarginBounds.Left;
int HeaderHeight=0;
try
{
// Before starting first page, it saves
// Width & Height of Headers and CoulmnType
if (PageNo == 1)
{
foreach (DataGridViewColumn GridCol in dgv.Columns)
{
if (!GridCol.Visible) continue;
// Skip if the current column not selected
if (!PrintDGV.SelectedColumns.Contains(
GridCol.HeaderText)) continue;
// Detemining whether the columns
// are fitted to the page or not.
if (FitToPageWidth)
tmpWidth = (int)(Math.Floor((double)(
(double)GridCol.Width /
(double)TotalWidth * (double)TotalWidth *
((double)e.MarginBounds.Width /
(double)TotalWidth))));
else
tmpWidth = GridCol.Width;
HeaderHeight =
(int)(e.Graphics.MeasureString(GridCol.HeaderText,
GridCol.InheritedStyle.Font, tmpWidth).Height) + 11;
// Save width & height of headres and ColumnType
ColumnLefts.Add(tmpLeft);
ColumnWidths.Add(tmpWidth);
ColumnTypes.Add(GridCol.GetType());
tmpLeft += tmpWidth;
}
}
// Printing Current Page, Row by Row
while (RowPos <= dgv.Rows.Count - 1)
{
DataGridViewRow GridRow = dgv.Rows[RowPos];
if (GridRow.IsNewRow || (!PrintAllRows && !GridRow.Selected))
{
RowPos++;
continue;
}
CellHeight = GridRow.Height;
if (tmpTop + CellHeight >=
e.MarginBounds.Height + e.MarginBounds.Top)
{
DrawFooter(e, RowsPerPage);
NewPage = true;
PageNo++;
e.HasMorePages = true;
return;
}
else
{
if (NewPage)
{
// Draw Print Title
e.Graphics.DrawString(PrintTitle,
new Font(dgv.Font, FontStyle.Bold),
Brushes.Black, e.MarginBounds.Left,
e.MarginBounds.Top -
e.Graphics.MeasureString(PrintTitle,
new Font(dgv.Font,
FontStyle.Bold),
e.MarginBounds.Width).Height - 13);
String s = DateTime.Now.ToLongDateString() + " " +
DateTime.Now.ToShortTimeString();
// Draw Time and Date
e.Graphics.DrawString(s,
new Font(dgv.Font, FontStyle.Bold),
Brushes.Black, e.MarginBounds.Left +
(e.MarginBounds.Width -
e.Graphics.MeasureString(s, new Font(dgv.Font,
FontStyle.Bold), e.MarginBounds.Width).Width),
e.MarginBounds.Top -
e.Graphics.MeasureString(PrintTitle,
new Font(new Font(dgv.Font,
FontStyle.Bold), FontStyle.Bold),
e.MarginBounds.Width).Height - 13);
// Draw Headers
tmpTop = e.MarginBounds.Top;
i = 0;
foreach (DataGridViewColumn GridCol in dgv.Columns)
{
if (!GridCol.Visible) continue;
if (!PrintDGV.SelectedColumns.Contains(
GridCol.HeaderText))
continue;
e.Graphics.FillRectangle(new
SolidBrush(Color.LightGray),
new Rectangle((int) ColumnLefts[i], tmpTop,
(int)ColumnWidths[i], HeaderHeight));
e.Graphics.DrawRectangle(Pens.Black,
new Rectangle((int) ColumnLefts[i], tmpTop,
(int)ColumnWidths[i], HeaderHeight));
e.Graphics.DrawString(GridCol.HeaderText,
GridCol.InheritedStyle.Font,
new SolidBrush(GridCol.InheritedStyle.ForeColor),
new RectangleF((int)ColumnLefts[i], tmpTop,
(int)ColumnWidths[i], HeaderHeight), StrFormat);
i++;
}
NewPage = false;
tmpTop += HeaderHeight;
}
// Draw Columns Contents
i = 0;
foreach (DataGridViewCell Cel in GridRow.Cells)
{
if (!Cel.OwningColumn.Visible) continue;
if (!SelectedColumns.Contains(
Cel.OwningColumn.HeaderText))
continue;
// For the TextBox Column
if (((Type) ColumnTypes[i]).Name ==
"DataGridViewTextBoxColumn" ||
((Type) ColumnTypes[i]).Name ==
"DataGridViewLinkColumn")
{
e.Graphics.DrawString(Cel.Value.ToString(),
Cel.InheritedStyle.Font,
new SolidBrush(Cel.InheritedStyle.ForeColor),
new RectangleF((int)ColumnLefts[i],
(float)tmpTop,
(int)ColumnWidths[i],
(float)CellHeight), StrFormat);
}
// For the Button Column
else if (((Type) ColumnTypes[i]).Name ==
"DataGridViewButtonColumn")
{
CellButton.Text = Cel.Value.ToString();
CellButton.Size = new Size((int)ColumnWidths[i],
CellHeight);
Bitmap bmp = new Bitmap(CellButton.Width,
CellButton.Height);
CellButton.DrawToBitmap(bmp, new Rectangle(0, 0,
bmp.Width, bmp.Height));
e.Graphics.DrawImage(bmp,
new Point((int)ColumnLefts[i], tmpTop));
}
// For the CheckBox Column
else if (((Type) ColumnTypes[i]).Name ==
"DataGridViewCheckBoxColumn")
{
CellCheckBox.Size = new Size(14, 14);
CellCheckBox.Checked = (bool)Cel.Value;
Bitmap bmp = new Bitmap((int)ColumnWidths[i],
CellHeight);
Graphics tmpGraphics = Graphics.FromImage(bmp);
tmpGraphics.FillRectangle(Brushes.White,
new Rectangle(0, 0,
bmp.Width, bmp.Height));
CellCheckBox.DrawToBitmap(bmp,
new Rectangle((int)((bmp.Width -
CellCheckBox.Width) / 2),
(int)((bmp.Height - CellCheckBox.Height) / 2),
CellCheckBox.Width, CellCheckBox.Height));
e.Graphics.DrawImage(bmp,
new Point((int)ColumnLefts[i], tmpTop));
}
// For the ComboBox Column
else if (((Type) ColumnTypes[i]).Name ==
"DataGridViewComboBoxColumn")
{
CellComboBox.Size = new Size((int)ColumnWidths[i],
CellHeight);
Bitmap bmp = new Bitmap(CellComboBox.Width,
CellComboBox.Height);
CellComboBox.DrawToBitmap(bmp, new Rectangle(0, 0,
bmp.Width, bmp.Height));
e.Graphics.DrawImage(bmp,
new Point((int)ColumnLefts[i], tmpTop));
e.Graphics.DrawString(Cel.Value.ToString(),
Cel.InheritedStyle.Font,
new SolidBrush(Cel.InheritedStyle.ForeColor),
new RectangleF((int)ColumnLefts[i] + 1,
tmpTop, (int)ColumnWidths[i]
- 16, CellHeight), StrFormatComboBox);
}
// For the Image Column
else if (((Type) ColumnTypes[i]).Name ==
"DataGridViewImageColumn")
{
Rectangle CelSize = new Rectangle((int)ColumnLefts[i],
tmpTop, (int)ColumnWidths[i], CellHeight);
Size ImgSize = ((Image)(Cel.FormattedValue)).Size;
e.Graphics.DrawImage((Image)Cel.FormattedValue,
new Rectangle((int)ColumnLefts[i] +
(int)((CelSize.Width - ImgSize.Width) / 2),
tmpTop + (int)((CelSize.Height -
ImgSize.Height) / 2),
((Image)(Cel.FormattedValue)).Width,
((Image)(Cel.FormattedValue)).Height));
}
// Drawing Cells Borders
e.Graphics.DrawRectangle(Pens.Black,
new Rectangle((int)ColumnLefts[i],
tmpTop, (int)ColumnWidths[i], CellHeight));
i++;
}
tmpTop += CellHeight;
}
RowPos++;
// For the first page it calculates Rows per Page
if (PageNo == 1) RowsPerPage++;
}
if (RowsPerPage == 0) return;
// Write Footer (Page Number)
DrawFooter(e, RowsPerPage);
e.HasMorePages = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
History
Two bugs fixed (22 Feb 2007):
- The number of pages in the C# code was wrong.
- For the pages after the first page, the column headers were overwriting the first row ( both in the C# code and the VB code).