????????DataGrid 是 .NET 架構中一個功能極其豐富的組件,或許也是最復雜的組件之一。寫這篇文章是為了回答“我到底該如何打印 DataGrid 及其內容”這個問題。最初即興的建議是使用我的屏幕截圖文章來截取表單,但這當然無法解決打印 DataGrid 中虛擬顯示的無數行數據的問題。后來想了想,這應該很簡單,我只需使用 GDI+ 遍歷 DataGrid 中的行并打印其內容即可。其實,DataGrid 比這更復雜一些,因為它本身并不包含數據。數據包含在 DataSet 中。因此,最終確定的方法是從 DataGrid 中捕獲顏色和字體屬性用于打印輸出,并從 DataSet 中捕獲行中的信息。為了將 DataGridPrinter 的繪制功能封裝到 Printer 中,我創建了 DataGridPrinter 類,如下圖 2 所示。此類將 DataGrid、PrintDocument 和 DataTable 傳遞給其構造函數,并利用這些對象將 DataGrid 繪制到打印機。
圖 1. Northwind DataGrid 的打印預覽
圖 2. DataGridPrinter 類 UML 設計(使用 WithClass 2000 進行逆向工程)
????????DataGridPrinter 在表單的構造函數中構建,因此它可以被所有打印功能(打印、打印預覽等)使用。下面是構建 DataGridPrinter 的代碼:
void SetupGridPrinter() ?
{ ?
? ? dataGridPrinter1 =new DataGridPrinter(dataGrid1, printDocument1, ?
? ? dataSet11.Customers); ?
} ?
一旦構建了 DataGridPrinter,您就可以通過在 Print Page 事件處理程序中調用其 DrawDataGrid 方法將 DataGrid 繪制到打印機上:
private void printDocument1_PrintPage(object sender,System.Drawing.Printing.PrintPageEventArgs e) ?
{ ?
? ? Graphics g = e.Graphics; ?
? ? // Draw a label title for the grid ?
? ? DrawTopLabel(g); ?
? ? // draw the datagrid using the DrawDataGrid method passing the Graphics surface ?
? ? bool more = dataGridPrinter1.DrawDataGrid(g); ?
? ? // if there are more pages, set the flag to cause the form to trigger another print page event ?
? ? if (more == true) ?
? ? { ?
? ? ? ? e.HasMorePages =true; ?
? ? ? ? dataGridPrinter1.PageNumber++; ?
? ? } ?
} ?
????????PrintPage 事件由 PrintDocument 中的 Print 方法和 PrintPreviewDialog 的 ShowDialog 方法觸發。下面是窗體將 DataGrid 打印到打印機的方法:
private void PrintMenu_Click(object sender, System.EventArgs e) ?
{ ?
? ? // Initialize the datagrid page and row properties ?
? ? dataGridPrinter1.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(); ?
? ? } ?
} ?
????????現在讓我們來看看 DataGridPrinter 方法的內部實現。DataGridPrinter 類中有兩個主要方法用于執行所有繪制操作:DrawHeader 和 DrawRows。這兩個方法都從 DataGrid 和 DataTable 中提取信息來繪制 DataGrid。以下是繪制 DataGrid 行的方法:
public bool DrawRows(Graphics g) ?
? ? { ?
? ? ? ? try ?
? ? ? ? { ?
? ? ? ? ? ? int lastRowBottom = TopMargin; ?
? ? ? ? ? ? // Create an array to save the horizontal positions for drawing horizontal gridlines ?
? ? ? ? ? ? ArrayList Lines = new ArrayList(); ?
? ? ? ? ? ? // form brushes based on the color properties of the DataGrid ?
? ? ? ? ? ? // These brushes will be used to draw the grid borders and cells ?
? ? ? ? ? ? SolidBrush 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 width ?
? ? ? ? ? ? StringFormat 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 ?
? ? ? ? ? ? // DataGrid ?
? ? ? ? ? ? int 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 DataTable ?
? ? ? ? ? ? ? ? DataRow dr = TheTable.Rows[i]; ?
? ? ? ? ? ? ? ? int startxposition = TheDataGrid.Location.X; ?
? ? ? ? ? ? ? ? // Calculate the row boundary based on teh RowCount and offsets into the page ?
? ? ? ? ? ? ? ? RowBounds.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 lines ?
? ? ? ? ? ? ? ? Lines.Add(RowBounds.Bottom); ?
? ? ? ? ? ? ? ? // paint rows differently for alternate row colors ?
? ? ? ? ? ? ? ? if (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 row ?
? ? ? ? ? ? ? ? if (startxposition + columnwidth <= PageWidth) ?
? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? g.DrawString(dr[j].ToString(), TheDataGrid.Font, ForeBrush, cellbounds, cellformat); ?
? ? ? ? ? ? ? ? ? ? lastRowBottom = (int)cellbounds.Bottom; ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? // increment the column position ?
? ? ? ? ? ? ? ? startxposition = startxposition + columnwidth; ?
? ? ? ? ? ? } ?
? ? ? ? ? ? RowCount++; ?
? ? ? ? ? ? // when we've reached the bottom of the page, draw the horizontal and vertical grid lines and return true ?
? ? ? ? if (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 gridlines and return false ?
? ? DrawHorizontalLines(g, Lines); ?
? ? DrawVerticalGridLines(g, TheLinePen, columnwidth, lastRowBottom); ?
? ? return false; ?
} ?
catch (Exception ex) ?
{ ?
MessageBox.Show(ex.Message.ToString()); ?
return false; ?
} ?
????????該方法遍歷 DataTable 中的每一行并繪制數據。該方法使用 DataGrid 的屬性,用適當的顏色繪制每一行,并使用 DataGrid 的字體繪制每個字符串。如果該方法到達頁面底部,則會中斷并返回 true,以便將 DataGrid 的剩余部分打印到下一頁。
改進:
利用???????? DataGrid 的 TableStyles 屬性中存儲的 DataGridColumnStyle 類,可以極大地改進此類。這些屬性允許您為某些列指定不同的列寬以及不同的文本對齊方式。
如果您喜歡此文章,請收藏、點贊、評論,謝謝,祝您快樂每一天。?