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;
-
- int cellHeight = GetCellBounds(1, 1).Height;
-
- int rightExtent = GetCellBounds(1, NumberOfColumns).Right + Bounds.X;
- int bottomExtent = GetCellBounds(NumberOfRows, 1).Bottom + Bounds.Y;
-
- StringFormat sfcenter = new StringFormat();
- sfcenter.Alignment = StringAlignment.Center;
-
- 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);
- 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);
- }
-
- 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);
- }
- }
-
- 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);
- }
- }
- }
-
- 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) {
-
- Rectangle r = listView1.Items[aRow - 1].Bounds;
-
- int colWidth = listView1.Columns[aColumn - 1].Width;
-
- int startCol = GetStartOfColumn(aColumn);
-
- r.X += startCol;
- r.Width = colWidth;
- return r;
- }
-
-
- 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)
- {
-
-
- 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) {
-
- printPreviewDialog1.SetBounds(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width + 50, ClientRectangle.Height + 50);
-
- printPreviewDialog1.PrintPreviewControl.Zoom = 1.0;
-
- 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.