The DataGrid is a highly versatile component of the .NET architecture and probably one of the most complex components. I wrote this article in response to the question, "How the heck do I print out a DataGrid and its contents". My first off the cuff suggestion was to capture the form using my screen capture article, but this of course does not solve the problem of printing out the umpteen rows being virtually displayed in the DataGrid. Then I thought to myself, this should be easy, I'll just use GDI+ and go through the rows in the DataGrid and print out its contents. Well the DataGrid is a bit more complex than that because it does not contain the data within itself. The data is contained within the DataSet. So the approach I settled on was to capture the color and font properties from the DataGrid for the print out, and the capture the information in the rows from the DataSet. In order to encapsulate the drawing of the DataGridPrinter to the Printer, I created the DataGridPrinter class shown in Figure 2 below. This class takes a DataGrid, a PrintDocument, and a DataTable passed to its constructor and utilizes these objects to draw the DataGrid to the printer.
Figure 1. The Print Preview of the Northwind DataGrid
Figure 2. DataGridPrinter Class UML Design (Reverse engineered using WithClass 2000)The DataGridPrinter is constructed in the constructor of the form so it can be utilized by all of the printing functions (print, print preview, etc.) Below is the code for constructing the DataGridPrinter:
void SetupGridPrinter()
{
dataGridPrinter1 = new DataGridPrinter(dataGrid1, printDocument1,
dataSet11.Customers);
}
void SetupGridPrinter()
{
dataGridPrinter1 = new DataGridPrinter(dataGrid1, printDocument1,
dataSet11.Customers);
}
Once the DataGridPrinter is constructed, you can have it draw the DataGrid to the printer by calling its DrawDataGrid method in the Print Page event handler:
private void printDocument1_PrintPage(object sender,System.Drawing.Printing.PrintPageEventArgs e)
{
Graphics g = e.Graphics;// Draw a label title for the gridDrawTopLabel(g);// draw the datagrid using the DrawDataGrid method passing the Graphics surfacebool more = dataGridPrinter1.DrawDataGrid(g);// if there are more pages, set the flag to cause the form to trigger another print
page eventif (more == true)
{
e.HasMorePages = true;
dataGridPrinter1.PageNumber++;
}
}
The PrintPage event is triggered by both the Print method in the PrintDocument and the PrintPreviewDialog's ShowDialog method. Below is the form's method for printing the DataGrid to the printer:
private void printDocument1_PrintPage(object sender,System.Drawing.Printing.PrintPageEventArgs e)
{
Graphics g = e.Graphics;// Draw a label title for the gridDrawTopLabel(g);// draw the datagrid using the DrawDataGrid method passing the Graphics surfacebool more = dataGridPrinter1.DrawDataGrid(g);// if there are more pages, set the flag to cause the form to trigger another print
page eventif (more == true)
{
e.HasMorePages = true;
dataGridPrinter1.PageNumber++;
}
}
The PrintPage event is triggered by both the Print method in the PrintDocument and the PrintPreviewDialog's ShowDialog method. Below is the form's method for printing the DataGrid to the printer:
private void PrintMenu_Click(object sender, System.EventArgs e)
{// Initialize the datagrid page and row propertiesdataGridPrinter1.PageNumber = 1;
dataGridPrinter1.RowCount = 0;// Show the Print Dialog to set properties and print the document after ok is pressed.if (printDialog1.ShowDialog() == DialogResult.OK)
{
printDocument1.Print();
}
}
Now let's take a look at the internals of the DataGridPrinter methods. There are two main methods in the DataGridPrinter class that do all the drawing: DrawHeader and DrawRows. Both these methods extract information from the DataGrid and the DataTable to draw the DataGrid. Below is the method for drawing the rows of the DataGrid:
public bool DrawRows(Graphics g)
{try{int lastRowBottom = TopMargin;// Create an array to save the horizontal positions for drawing horizontal gridlinesArrayList Lines = new ArrayList();// form brushes based on the color properties of the DataGrid
// These brushes will be used to draw the grid borders and cellsSolidBrush ForeBrush = new SolidBrush(TheDataGrid.ForeColor);
SolidBrush BackBrush = new SolidBrush(TheDataGrid.BackColor);
SolidBrush AlternatingBackBrush = new SolidBrush
TheDataGrid.AlternatingBackColor);
Pen TheLinePen = new Pen(TheDataGrid.GridLineColor, 1);// Create a format for the cell so that the string in the cell is cut off at the end of
the column widthStringFormat cellformat = new StringFormat();
cellformat.Trimming = StringTrimming.EllipsisCharacter;
cellformat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit;// calculate the column width based on the width of the printed page and the # of
columns in the DataTable
// Note: Column Widths can be made variable in a future program by playing with
the GridColumnStyles of the
// DataGridint columnwidth = PageWidth/TheTable.Columns.Count;// set the initial row count, this will start at 0 for the first page, and be a different
value for the 2nd, 3rd, 4th, etc.
// pages.int initialRowCount = RowCount;
RectangleF RowBounds = new RectangleF(0, 0, 0, 0);// draw the rows of the table for (int i = initialRowCount; i < TheTable.Rows.Count; i++)
{// get the next DataRow in the DataTableDataRow dr = TheTable.Rows[i];int startxposition = TheDataGrid.Location.X;// Calculate the row boundary based on teh RowCount and offsets into the pageRowBounds.X = TheDataGrid.Location.X;RowBounds.Y = TheDataGrid.Location.Y +
TopMargin + ((RowCount - initialRowCount)+1) * (TheDataGrid.Font.SizeInPoints +
kVerticalCellLeeway);
RowBounds.Height = TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway;
RowBounds.Width = PageWidth;// save the vertical row positions for drawing grid linesLines.Add(RowBounds.Bottom);// paint rows differently for alternate row colorsif (i%2 == 0)
{
g.FillRectangle(BackBrush, RowBounds);
}else{
g.FillRectangle(AlternatingBackBrush, RowBounds);
}// Go through each column in the row and draw the information from the
DataRowfor (int j = 0; j < TheTable.Columns.Count; j++)
{
RectangleF cellbounds = new RectangleF(startxposition,
TheDataGrid.Location.Y + TopMargin + ((RowCount - initialRowCount) + 1) *
(TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway),
columnwidth,
TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway);/ draw the data at the next position in the rowif (startxposition + columnwidth <= PageWidth)
{
g.DrawString(dr[j].ToString(), TheDataGrid.Font, ForeBrush, cellbounds,cellformat);
lastRowBottom = (int)cellbounds.Bottom;
}// increment the column positionstartxposition = startxposition + columnwidth;
}
RowCount++;// when we've reached the bottom of the page, draw the horizontal and vertical
grid lines and return trueif (RowCount * (TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway) >
PageHeight * PageNumber) -
(BottomMargin+TopMargin))
{
DrawHorizontalLines(g, Lines);DrawVerticalGridLines(g, TheLinePen, columnwidth,
lastRowBottom);return true;
}
}// when we've reached the end of the table, draw the horizontal and vertical grid
lines and return falseDrawHorizontalLines(g, Lines);
DrawVerticalGridLines(g, TheLinePen, columnwidth, lastRowBottom);return false;
}catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());return false;
}
}
The method goes through each row in the DataTable and draws the data. The method uses the properties of the DataGrid to paint each row with the appropriate colors and draw each string with the DataGrid's font. If the method reaches the bottom of the page, it breaks and returns true so that the rest of the DataGrid can be printed
{// Initialize the datagrid page and row propertiesdataGridPrinter1.PageNumber = 1;
dataGridPrinter1.RowCount = 0;// Show the Print Dialog to set properties and print the document after ok is pressed.if (printDialog1.ShowDialog() == DialogResult.OK)
{
printDocument1.Print();
}
}
Now let's take a look at the internals of the DataGridPrinter methods. There are two main methods in the DataGridPrinter class that do all the drawing: DrawHeader and DrawRows. Both these methods extract information from the DataGrid and the DataTable to draw the DataGrid. Below is the method for drawing the rows of the DataGrid:
public bool DrawRows(Graphics g)
{try{int lastRowBottom = TopMargin;// Create an array to save the horizontal positions for drawing horizontal gridlinesArrayList Lines = new ArrayList();// form brushes based on the color properties of the DataGrid
// These brushes will be used to draw the grid borders and cellsSolidBrush ForeBrush = new SolidBrush(TheDataGrid.ForeColor);
SolidBrush BackBrush = new SolidBrush(TheDataGrid.BackColor);
SolidBrush AlternatingBackBrush = new SolidBrush
TheDataGrid.AlternatingBackColor);
Pen TheLinePen = new Pen(TheDataGrid.GridLineColor, 1);// Create a format for the cell so that the string in the cell is cut off at the end of
the column widthStringFormat cellformat = new StringFormat();
cellformat.Trimming = StringTrimming.EllipsisCharacter;
cellformat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit;// calculate the column width based on the width of the printed page and the # of
columns in the DataTable
// Note: Column Widths can be made variable in a future program by playing with
the GridColumnStyles of the
// DataGridint columnwidth = PageWidth/TheTable.Columns.Count;// set the initial row count, this will start at 0 for the first page, and be a different
value for the 2nd, 3rd, 4th, etc.
// pages.int initialRowCount = RowCount;
RectangleF RowBounds = new RectangleF(0, 0, 0, 0);// draw the rows of the table for (int i = initialRowCount; i < TheTable.Rows.Count; i++)
{// get the next DataRow in the DataTableDataRow dr = TheTable.Rows[i];int startxposition = TheDataGrid.Location.X;// Calculate the row boundary based on teh RowCount and offsets into the pageRowBounds.X = TheDataGrid.Location.X;RowBounds.Y = TheDataGrid.Location.Y +
TopMargin + ((RowCount - initialRowCount)+1) * (TheDataGrid.Font.SizeInPoints +
kVerticalCellLeeway);
RowBounds.Height = TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway;
RowBounds.Width = PageWidth;// save the vertical row positions for drawing grid linesLines.Add(RowBounds.Bottom);// paint rows differently for alternate row colorsif (i%2 == 0)
{
g.FillRectangle(BackBrush, RowBounds);
}else{
g.FillRectangle(AlternatingBackBrush, RowBounds);
}// Go through each column in the row and draw the information from the
DataRowfor (int j = 0; j < TheTable.Columns.Count; j++)
{
RectangleF cellbounds = new RectangleF(startxposition,
TheDataGrid.Location.Y + TopMargin + ((RowCount - initialRowCount) + 1) *
(TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway),
columnwidth,
TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway);/ draw the data at the next position in the rowif (startxposition + columnwidth <= PageWidth)
{
g.DrawString(dr[j].ToString(), TheDataGrid.Font, ForeBrush, cellbounds,cellformat);
lastRowBottom = (int)cellbounds.Bottom;
}// increment the column positionstartxposition = startxposition + columnwidth;
}
RowCount++;// when we've reached the bottom of the page, draw the horizontal and vertical
grid lines and return trueif (RowCount * (TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway) >
PageHeight * PageNumber) -
(BottomMargin+TopMargin))
{
DrawHorizontalLines(g, Lines);DrawVerticalGridLines(g, TheLinePen, columnwidth,
lastRowBottom);return true;
}
}// when we've reached the end of the table, draw the horizontal and vertical grid
lines and return falseDrawHorizontalLines(g, Lines);
DrawVerticalGridLines(g, TheLinePen, columnwidth, lastRowBottom);return false;
}catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());return false;
}
}
The method goes through each row in the DataTable and draws the data. The method uses the properties of the DataGrid to paint each row with the appropriate colors and draw each string with the DataGrid's font. If the method reaches the bottom of the page, it breaks and returns true so that the rest of the DataGrid can be printed
No comments:
Post a Comment