Editable GridView Control in C# and .NET - Part-III Printing the GridView


In our last two articles, we talked about how to create an editable GridView and how to make it persistent in XML. This article covers how we can take advantage of the .NET framework to print out our GridView to an available printer.

(To see the other articles in our series,  Go to Editable GridView Control in C# and .NET - Part I  and Editable GridView Control in C# and .NET - Part II).

 

Figure 1 - Printing the GridView

.Net gives us a few controls that make printing quite easy and handle much of the details of driving the printer internally. In fact,  most of our responsibility in printing the GridView control lies in graphically drawing its contents. The drawing of the GridView control is accomplished using GDI+.  Below is the routine for drawing the GridView. The Print method takes a Graphics object (in the case of printing this is a Printer Graphics Object).  It utilizes the Graphics Object to draw the lines, colors and text necessary to construct our grid.  The hard part of drawing the grid is mathematically mapping what is on our current grid control to our drawing surface. Also we have to figure out what methods we can use from the ListView to give us our cell borders and locations of them. The Print method first prints the column header, then the cells of the GridView, and finally the gridlines and boundary of the GridView.

[DispId(22)]
public void Print(Graphics g)
{
Rectangle frame =
this.Bounds;
// Calculate the Height of a Cell in the Grid
int cellHeight = GetCellBounds(1, 1).Height;
// Calculate the values needed to locate the Extents of the Grid
int rightExtent = GetCellBounds(1, NumberOfColumns).Right + Bounds.X;
int bottomExtent = GetCellBounds(NumberOfRows, 1).Bottom + Bounds.Y;
// Create a string format object that aligns the string in the center of the rectangle
StringFormat sfcenter = new StringFormat();
sfcenter.Alignment = StringAlignment.Center;
// Loop through all the columns and draw the column header text and background color
for (int j = 1; j <= this.NumberOfColumns; j++)
{
string strText = listView1.Columns[j-1].Text;
Color aColor = Color.LightGray;
Color aTextColor = Color.Black;
Rectangle cellFrame = GetCellBounds(1, j);
cellFrame.Offset(Bounds.X, Bounds.Y);
// offset to the absolute position on the form
int nTop =cellFrame.Top;
cellFrame.Offset(0, - (cellFrame.Y - Bounds.Y));
cellFrame.Height += (nTop - Bounds.Top);
g.FillRectangle(
new SolidBrush(aColor), cellFrame);g.DrawString(strText,
listView1.Font,
new SolidBrush(aTextColor), cellFrame, sfcenter);
}
// Loop through all of the cells in the grid, draw background color, text color, and text for each cell
for (int i = 1; i <= this.NumberOfRows; i++)
{
for (int j = 1; j <= this.NumberOfColumns; j++)
{
string strText = this.GetCell(i, j);
Color aColor =
this.GetCellColor(i, j);
Color aTextColor =
this.GetCellTextColor(i, j);
Rectangle cellFrame = GetCellBounds(i, j);
cellFrame.Offset(Bounds.X, Bounds.Y);
g.FillRectangle(
new SolidBrush(aColor), cellFrame);
g.DrawString(strText, listView1.Font,
new SolidBrush(aTextColor), cellFrame,
sfcenter);
}
}
// Loop through all the cells in the grid again. Draw all of the lines that form the
borders for the cells.
for (int i = 1; i <= this.NumberOfRows; i++)
{
Rectangle r1 = GetCellBounds(i, 1);
r1.Offset(Bounds.X, Bounds.Y);
g.DrawLine(Pens.Black, Bounds.X, r1.Top, rightExtent, r1.Top);
for (int j = 1; j <= this.NumberOfColumns; j++)
{
Rectangle cellFrame = GetCellBounds(i, j);
cellFrame.Offset(Bounds.X, Bounds.Y);
if (i == 1)
{
g.DrawLine(Pens.Black, cellFrame.Left, Bounds.Y, cellFrame.Left, bottomExtent);
}
if (j == NumberOfColumns)
{
g.DrawLine(Pens.Black, cellFrame.Right, Bounds.Y, cellFrame.Right, bottomExtent);
}
}
}
// Draw the Border around the entire grid
Rectangle rFrame = Bounds;
rFrame.Width -= (Bounds.Right - rightExtent);
rFrame.Height -= (Bounds.Bottom -bottomExtent);
g.DrawRectangle(Pens.Black, rFrame);
}

As shown in the code above, we created a method GetCellBounds that gets the absolute boundaries of the cell with the listView as a frame of reference. This method utilizes the Bounds property of the ListViewItem class along with the Width property of the ColumnHeader class to determine the location of the cell in the grid.

public Rectangle GetCellBounds(int aRow, int aColumn)
{
// Get the boundary of an entire row in the ListView corresponding to the aRow index passed
Rectangle r = listView1.Items[aRow - 1].Bounds;
// Get the width of the column indicated by the column index passed
int colWidth = listView1.Columns[aColumn - 1].Width;
// Find the starting position of the column using the GetStartOfColumn function in the GridView
int startCol = GetStartOfColumn(aColumn);
// Shrink the ListView Item Boundary rectangle to only contain the boundary of the cell passed
r.X += startCol;
r.Width = colWidth;
return r;
}
// The GetStartOfColumn computes a sum of all the column widths up to (but not including) the index of
// the column passed to the function. This value is the position of the column in the grid
public int GetStartOfColumn(int aColumn)
{
int sum = 0;
for (int i = 1; i < aColumn; i++)
{
sum += listView1.Columns[i - 1].Width;
}
return sum;
}

That's all there is to drawing the GridView in GDI+. Now we need to take advanage of the Print method in the GridView to print our GridView to the printer. In our form application we need to drag a PrintDocument object and a PrintPreviewDialog object onto the form. This gives us access to the objects printDocument1 and printPreviewDialog1 (although you can change these names if you wish in the properties window. In the properties window of printPreviewDialog1, assign the Document property to printDocument as shown in Figure 2 below:

Figure 2 - Assigning the print document to the print preview dialog

Double click on the printDocument1 object shown in your design view to wire up the PrintPage event shown in the property window of printDocument1  in Figure 3.

Figure 3 - Wiring up the PrintPage event

This will create an event handler called printDocument1_PrintPage which you can now place the necessary code to print your GridView shown below:

private void printDocument1_PrintPage(object sender,System.Drawing.Printing.PrintPageEventArgs e)
{
// Print the GridView by passing the Printer Graphics Object thats passed into the PrintPage event handler into
// the Print method of the GridView.
gridView1.Print(e.Graphics);
}

Now to activate the PrintPage event, we need to either print or bring up a print preview dialog to simulate a print. Below is the code for bringing up the print preview dialog from a menu event handler called PrintMenu_Click. Note that we size the print preview dialog to be at least the size of the form and we force a zoom to 100%.

private void PrintMenu_Click(object sender, System.EventArgs e)
{
// size the dialog to be as big as the form (+ a little more)
printPreviewDialog1.SetBounds(ClientRectangle.X, ClientRectangle.Y,ClientRectangle.Width + 50, ClientRectangle.Height + 50);
// Zoom the preview to 100%
printPreviewDialog1.PrintPreviewControl.Zoom = 1.0;
// Show the dialog
if (printPreviewDialog1.ShowDialog() == DialogResult.OK)
{
}
}

Once you bring up the print preview, you can print directly from this dialog.  If you wish to print directly to the printer without bringing up the preview dialog you can replace the code in the event handler above with the following:

private void PrintMenu_Click(object sender, System.EventArgs e)
{
printDocument1.Print();
}

Either The Print method of the PrintDocument class or the ShowDialog of the PrintPreviewDialog class can be used to activate the PrintPage event.

Summary

In this article, we showed you how you can take your GridView control and output an image of it to the printer utilizing GDI+ and the print components of the .NET framework. In Part IV of this series, we'll show you how to export the contents of your GridView to an Excel spreadsheet.

Up Next
    Ebook Download
    View all
    Learn
    View all