using System; using System.Text; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Drawing.Printing; using System.Data; using System.Windows.Forms; publicclass DataGridViewPrinter ...{ /**////<summary> /// The DataGridView Control which will be printed ///</summary> private DataGridView TheDataGridView; /**////<summary> /// The PrintDocument to be used for printing ///</summary> private PrintDocument ThePrintDocument; /**////<summary> /// Determine if the report will be printed in the Top-Center of the page ///</summary> privatebool IsCenterOnPage; /**////<summary> /// Determine if the page contain title text ///</summary> privatebool IsWithTitle; /**////<summary> /// The title text to be printed in each page (if IsWithTitle is set to true) ///</summary> privatestring TheTitleText; /**////<summary> /// The font to be used with the title text (if IsWithTitle is set to true) ///</summary> private Font TheTitleFont; /**////<summary> /// The color to be used with the title text (if IsWithTitle is set to true) ///</summary> private Color TheTitleColor; /**////<summary> /// Determine if paging is used ///</summary> privatebool IsWithPaging; /**////<summary> /// A static parameter that keep track on which Row (in the DataGridView control) that should be printed ///</summary> staticint CurrentRow; /**////<summary> /// A static parameter that representing the pages ///</summary> staticint PageNumber; privateint PageWidth; privateint PageHeight; privateint LeftMargin; privateint TopMargin; privateint RightMargin; privateint BottomMargin; /**////<summary> /// A parameter that keep track on the y coordinate of the page /// so the next object to be printed will start from this y coordinate ///</summary> privatefloat CurrentY; privatefloat RowHeaderHeight; private List<float> RowsHeight; private List<float> ColumnsWidth; privatefloat TheDataGridViewWidth; /**////<summary> /// Maintain a generic list to hold start/stop points for the column printing /// This will be used for wrapping in situations where the DataGridView will not fit on a single page ///</summary> private List<int[]> mColumnPoints; private List<float> mColumnPointsWidth; privateint mColumnPoint; /**////<summary> /// The printer's constructor ///</summary> ///<param name="aDataGridView">A DataGridView will be printed</param> ///<param name="aPrintDocument">A printDocument to be used for printing</param> ///<param name="CenterOnPage">A variable representing print the report in the center or not</param> ///<param name="WithTitle">A variable representing print the report title or not</param> ///<param name="aTitleText">A variable representing the report's title</param> ///<param name="aTitleFont">A variable representing the title's font of the report</param> ///<param name="aTitleColor">A variable representing the title's color of the report</param> ///<param name="WithPaging">A variable representing the paging is used or not</param> public DataGridViewPrinter(DataGridView aDataGridView, PrintDocument aPrintDocument, bool CenterOnPage, bool WithTitle, string aTitleText, Font aTitleFont, Color aTitleColor, bool WithPaging) ...{ TheDataGridView = aDataGridView; ThePrintDocument = aPrintDocument; IsCenterOnPage = CenterOnPage; IsWithTitle = WithTitle; TheTitleText = aTitleText; TheTitleFont = aTitleFont; TheTitleColor = aTitleColor; IsWithPaging = WithPaging; PageNumber =0; RowsHeight =new List<float>(); ColumnsWidth =new List<float>(); mColumnPoints =new List<int[]>(); mColumnPointsWidth =new List<float>(); // Claculating the PageWidth and the PageHeight if (!ThePrintDocument.DefaultPageSettings.Landscape) ...{ PageWidth = ThePrintDocument.DefaultPageSettings.PaperSize.Width; PageHeight = ThePrintDocument.DefaultPageSettings.PaperSize.Height; } else ...{ PageHeight = ThePrintDocument.DefaultPageSettings.PaperSize.Width; PageWidth = ThePrintDocument.DefaultPageSettings.PaperSize.Height; } // Claculating the page margins LeftMargin = ThePrintDocument.DefaultPageSettings.Margins.Left; TopMargin = ThePrintDocument.DefaultPageSettings.Margins.Top; RightMargin = ThePrintDocument.DefaultPageSettings.Margins.Right; BottomMargin = ThePrintDocument.DefaultPageSettings.Margins.Bottom; // First, the current row to be printed is the first row in the DataGridView control CurrentRow =0; } /**////<summary> /// An overload constructor ///</summary> ///<param name="aDataGridView">A DataGridView will be printed</param> ///<param name="CenterOnPage">A variable representing print the report in the center or not</param> ///<param name="WithTitle">A variable representing print the report title or not</param> ///<param name="aTitleText">A variable representing the report's title</param> ///<param name="aTitleFont">A variable representing the title's font of the report</param> ///<param name="aTitleColor">A variable representing the title's color of the report</param> ///<param name="WithPaging">A variable representing the paging is used or not</param> public DataGridViewPrinter(DataGridView aDataGridView, bool CenterOnPage, bool WithTitle, string aTitleText, Font aTitleFont, Color aTitleColor, bool WithPaging) ...{ ThePrintDocument =new PrintDocument(); ThePrintDocument.PrintPage +=new PrintPageEventHandler(PrintDocument_PrintPage); PrintDialog MyPrintDialog =new PrintDialog(); MyPrintDialog.AllowCurrentPage =false; MyPrintDialog.AllowPrintToFile =false; MyPrintDialog.AllowSelection =false; MyPrintDialog.AllowSomePages =false; MyPrintDialog.PrintToFile =false; MyPrintDialog.ShowHelp =false; MyPrintDialog.ShowNetwork =false; if (MyPrintDialog.ShowDialog() == DialogResult.OK) ...{ ThePrintDocument.DocumentName = aTitleText; ThePrintDocument.PrinterSettings = MyPrintDialog.PrinterSettings; ThePrintDocument.DefaultPageSettings = MyPrintDialog.PrinterSettings.DefaultPageSettings; ThePrintDocument.DefaultPageSettings.Margins =new Margins(5, 5, 40, 40); TheDataGridView = aDataGridView; IsCenterOnPage = CenterOnPage; IsWithTitle = WithTitle; TheTitleText = aTitleText; TheTitleFont = aTitleFont; TheTitleColor = aTitleColor; IsWithPaging = WithPaging; PageNumber =0; RowsHeight =new List<float>(); ColumnsWidth =new List<float>(); mColumnPoints =new List<int[]>(); mColumnPointsWidth =new List<float>(); // Claculating the PageWidth and the PageHeight if (!ThePrintDocument.DefaultPageSettings.Landscape) ...{ PageWidth = ThePrintDocument.DefaultPageSettings.PaperSize.Width; PageHeight = ThePrintDocument.DefaultPageSettings.PaperSize.Height; } else ...{ PageHeight = ThePrintDocument.DefaultPageSettings.PaperSize.Width; PageWidth = ThePrintDocument.DefaultPageSettings.PaperSize.Height; } // Claculating the page margins LeftMargin = ThePrintDocument.DefaultPageSettings.Margins.Left; TopMargin = ThePrintDocument.DefaultPageSettings.Margins.Top; RightMargin = ThePrintDocument.DefaultPageSettings.Margins.Right; BottomMargin = ThePrintDocument.DefaultPageSettings.Margins.Bottom; // First, the current row to be printed is the first row in the DataGridView control CurrentRow =0; PrintPreviewDialog printPreviewDialog =new PrintPreviewDialog(); printPreviewDialog.Document = ThePrintDocument; printPreviewDialog.ShowIcon =false; printPreviewDialog.Text = aTitleText +" Print Preview"; printPreviewDialog.ShowDialog(); } } /**////<summary> /// A PrintPageEventHandler ///</summary> ///<param name="sender"></param> ///<param name="e"></param> privatevoid PrintDocument_PrintPage(object sender, PrintPageEventArgs e) ...{ bool more =this.DrawDataGridView(e.Graphics); if (more) ...{ e.HasMorePages =true; } } /**////<summary> /// The function that calculate the height of each row (including the header row) /// the width of each column (according to the longest text in all its cells including the header cell) /// and the whole DataGridView width ///</summary> ///<param name="g"></param> privatevoid Calculate(Graphics g) ...{ // Just calculate once if (PageNumber ==0) ...{ SizeF tmpSize =new SizeF(); Font tmpFont; float tmpWidth; TheDataGridViewWidth =0; for (int i =0; i < TheDataGridView.Columns.Count; i++) ...{ tmpFont = TheDataGridView.ColumnHeadersDefaultCellStyle.Font; /**//*If there is no special HeaderFont style * then use the default DataGridView font style */ if (tmpFont ==null) tmpFont = TheDataGridView.DefaultCellStyle.Font; tmpSize = g.MeasureString(TheDataGridView.Columns[i].HeaderText, tmpFont); tmpWidth = tmpSize.Width; RowHeaderHeight = tmpSize.Height; for (int j =0; j < TheDataGridView.Rows.Count; j++) ...{ tmpFont = TheDataGridView.Rows[j].DefaultCellStyle.Font; /**//*If the there is no special font style of the CurrentRow * then use the default one associated with the DataGridView control */ if (tmpFont ==null) tmpFont = TheDataGridView.DefaultCellStyle.Font; tmpSize = g.MeasureString("Anything", tmpFont); RowsHeight.Add(tmpSize.Height); tmpSize = g.MeasureString(TheDataGridView.Rows[j].Cells[i].EditedFormattedValue.ToString(), tmpFont); if (tmpSize.Width > tmpWidth) tmpWidth = tmpSize.Width; } if (TheDataGridView.Columns[i].Visible) TheDataGridViewWidth += tmpWidth; ColumnsWidth.Add(tmpWidth); } /**//* Define the start/stop column points based on the page width and the DataGridView Width * We will use this to determine the columns which are drawn on each page and how wrapping will be handled * By default, the wrapping will occurr such that the maximum number of columns for a page will be determine */ int k; int mStartPoint =0; for (k =0; k < TheDataGridView.Columns.Count; k++) if (TheDataGridView.Columns[k].Visible) ...{ mStartPoint = k; break; } int mEndPoint = TheDataGridView.Columns.Count; for (k = TheDataGridView.Columns.Count -1; k >=0; k--) if (TheDataGridView.Columns[k].Visible) ...{ mEndPoint = k +1; break; } float mTempWidth = TheDataGridViewWidth; float mTempPrintArea = (float)PageWidth - (float)LeftMargin - (float)RightMargin; // We only care about handling where the total datagridview width is bigger then the print area if (TheDataGridViewWidth > mTempPrintArea) ...{ mTempWidth =0.0F; for (k =0; k < TheDataGridView.Columns.Count; k++) ...{ if (TheDataGridView.Columns[k].Visible) ...{ mTempWidth += ColumnsWidth[k]; /**//* If the width is bigger than the page area * then define a new column print range */ if (mTempWidth > mTempPrintArea) ...{ mTempWidth -= ColumnsWidth[k]; mColumnPoints.Add(newint[] ...{ mStartPoint, mEndPoint }); mColumnPointsWidth.Add(mTempWidth); mStartPoint = k; mTempWidth = ColumnsWidth[k]; } } // Our end point is actually one index above the current index mEndPoint = k +1; } } // Add the last set of columns mColumnPoints.Add(newint[] ...{ mStartPoint, mEndPoint }); mColumnPointsWidth.Add(mTempWidth); mColumnPoint =0; } } /**////<summary> /// The funtion that print the title, page number, and the header row ///</summary> ///<param name="g"></param> privatevoid DrawHeader(Graphics g) ...{ CurrentY = (float)TopMargin; // Printing the page number (if isWithPaging is set to true) if (IsWithPaging) ...{ PageNumber++; string PageString ="Page "+ PageNumber.ToString(); StringFormat PageStringFormat =new StringFormat(); PageStringFormat.Trimming = StringTrimming.Word; PageStringFormat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; PageStringFormat.Alignment = StringAlignment.Far; Font PageStringFont =new Font("Tahoma", 8, FontStyle.Regular, GraphicsUnit.Point); RectangleF PageStringRectangle =new RectangleF((float)LeftMargin, CurrentY, (float)PageWidth - (float)RightMargin - (float)LeftMargin, g.MeasureString(PageString, PageStringFont).Height); g.DrawString(PageString, PageStringFont, new SolidBrush(Color.Black), PageStringRectangle, PageStringFormat); CurrentY += g.MeasureString(PageString, PageStringFont).Height; } // Printing the title (if IsWithTitle is set to true) if (IsWithTitle) ...{ StringFormat TitleFormat =new StringFormat(); TitleFormat.Trimming = StringTrimming.Word; TitleFormat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; if (IsCenterOnPage) TitleFormat.Alignment = StringAlignment.Center; else TitleFormat.Alignment = StringAlignment.Near; RectangleF TitleRectangle =new RectangleF((float)LeftMargin, CurrentY, (float)PageWidth - (float)RightMargin - (float)LeftMargin, g.MeasureString(TheTitleText, TheTitleFont).Height); g.DrawString(TheTitleText, TheTitleFont, new SolidBrush(TheTitleColor), TitleRectangle, TitleFormat); CurrentY += g.MeasureString(TheTitleText, TheTitleFont).Height; } // Calculating the starting x coordinate that the printing process will start from float CurrentX = (float)LeftMargin; if (IsCenterOnPage) CurrentX += (((float)PageWidth - (float)RightMargin - (float)LeftMargin) - mColumnPointsWidth[mColumnPoint]) /2.0F; // Setting the HeaderFore style Color HeaderForeColor = TheDataGridView.ColumnHeadersDefaultCellStyle.ForeColor; if (HeaderForeColor.IsEmpty) // If there is no special HeaderFore style, then use the default DataGridView style HeaderForeColor = TheDataGridView.DefaultCellStyle.ForeColor; SolidBrush HeaderForeBrush =new SolidBrush(HeaderForeColor); // Setting the HeaderBack style Color HeaderBackColor = TheDataGridView.ColumnHeadersDefaultCellStyle.BackColor; // If there is no special HeaderBack style, then use the default DataGridView style if (HeaderBackColor.IsEmpty) HeaderBackColor = TheDataGridView.DefaultCellStyle.BackColor; SolidBrush HeaderBackBrush =new SolidBrush(HeaderBackColor); // Setting the LinePen that will be used to draw lines and rectangles (derived from the GridColor property of the DataGridView control) Pen TheLinePen =new Pen(TheDataGridView.GridColor, 1); // Setting the HeaderFont style Font HeaderFont = TheDataGridView.ColumnHeadersDefaultCellStyle.Font; if (HeaderFont ==null) // If there is no special HeaderFont style, then use the default DataGridView font style HeaderFont = TheDataGridView.DefaultCellStyle.Font; // Calculating and drawing the HeaderBounds RectangleF HeaderBounds =new RectangleF(CurrentX, CurrentY, mColumnPointsWidth[mColumnPoint], RowHeaderHeight); g.FillRectangle(HeaderBackBrush, HeaderBounds); // Setting the format that will be used to print each cell of the header row StringFormat CellFormat =new StringFormat(); CellFormat.Trimming = StringTrimming.Word; CellFormat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; // Printing each visible cell of the header row RectangleF CellBounds; float ColumnWidth; for (int i = (int)mColumnPoints[mColumnPoint].GetValue(0); i < (int)mColumnPoints[mColumnPoint].GetValue(1); i++) ...{ if (!TheDataGridView.Columns[i].Visible) continue; // If the column is not visible then ignore this iteration ColumnWidth = ColumnsWidth[i]; // Check the CurrentCell alignment and apply it to the CellFormat if (TheDataGridView.ColumnHeadersDefaultCellStyle.Alignment.ToString().Contains("Right")) CellFormat.Alignment = StringAlignment.Far; elseif (TheDataGridView.ColumnHeadersDefaultCellStyle.Alignment.ToString().Contains("Center")) CellFormat.Alignment = StringAlignment.Center; else CellFormat.Alignment = StringAlignment.Near; CellBounds =new RectangleF(CurrentX, CurrentY, ColumnWidth, RowHeaderHeight); // Printing the cell text g.DrawString(TheDataGridView.Columns[i].HeaderText, HeaderFont, HeaderForeBrush, CellBounds, CellFormat); // Drawing the cell bounds if (TheDataGridView.RowHeadersBorderStyle != DataGridViewHeaderBorderStyle.None) // Draw the cell border only if the HeaderBorderStyle is not None g.DrawRectangle(TheLinePen, CurrentX, CurrentY, ColumnWidth, RowHeaderHeight); CurrentX += ColumnWidth; } CurrentY += RowHeaderHeight; } /**////<summary> /// The function that print a bunch of rows that fit in one page /// When it returns true, meaning that there are more rows still not printed, so another PagePrint action is required /// When it returns false, meaning that all rows are printed (the CureentRow parameter reaches the last row of the DataGridView control) /// and no further PagePrint action is required ///</summary> ///<param name="g"></param> ///<returns>bool</returns> privatebool DrawRows(Graphics g) ...{ // Setting the LinePen that will be used to draw lines and rectangles (derived from the GridColor property of the DataGridView control) Pen TheLinePen =new Pen(TheDataGridView.GridColor, 1); // The style paramters that will be used to print each cell Font RowFont; Color RowForeColor; Color RowBackColor; SolidBrush RowForeBrush; SolidBrush RowBackBrush; SolidBrush RowAlternatingBackBrush; // Setting the format that will be used to print each cell StringFormat CellFormat =new StringFormat(); CellFormat.Trimming = StringTrimming.Word; CellFormat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit; // Printing each visible cell RectangleF RowBounds; float CurrentX; float ColumnWidth; while (CurrentRow < TheDataGridView.Rows.Count) ...{ // Print the cells of the CurrentRow only if that row is visible if (TheDataGridView.Rows[CurrentRow].Visible) ...{ // Setting the row font style RowFont = TheDataGridView.Rows[CurrentRow].DefaultCellStyle.Font; if (RowFont ==null) // If the there is no special font style of the CurrentRow, then use the default one associated with the DataGridView control RowFont = TheDataGridView.DefaultCellStyle.Font; // Setting the RowFore style RowForeColor = TheDataGridView.Rows[CurrentRow].DefaultCellStyle.ForeColor; if (RowForeColor.IsEmpty) // If the there is no special RowFore style of the CurrentRow, then use the default one associated with the DataGridView control RowForeColor = TheDataGridView.DefaultCellStyle.ForeColor; RowForeBrush =new SolidBrush(RowForeColor); // Setting the RowBack (for even rows) and the RowAlternatingBack (for odd rows) styles RowBackColor = TheDataGridView.Rows[CurrentRow].DefaultCellStyle.BackColor; /**//* If the there is no special RowBack style of the CurrentRow * then use the default one associated with the DataGridView control */ if (RowBackColor.IsEmpty) ...{ RowBackBrush =new SolidBrush(TheDataGridView.DefaultCellStyle.BackColor); RowAlternatingBackBrush =new SolidBrush(TheDataGridView.AlternatingRowsDefaultCellStyle.BackColor); } else// If the there is a special RowBack style of the CurrentRow, then use it for both the RowBack and the RowAlternatingBack styles ...{ RowBackBrush =new SolidBrush(RowBackColor); RowAlternatingBackBrush =new SolidBrush(RowBackColor); } // Calculating the starting x coordinate that the printing process will start from CurrentX = (float)LeftMargin; if (IsCenterOnPage) CurrentX += (((float)PageWidth - (float)RightMargin - (float)LeftMargin) - mColumnPointsWidth[mColumnPoint]) /2.0F; // Calculating the entire CurrentRow bounds RowBounds =new RectangleF(CurrentX, CurrentY, mColumnPointsWidth[mColumnPoint], RowsHeight[CurrentRow]); // Filling the back of the CurrentRow if (CurrentRow %2==0) g.FillRectangle(RowBackBrush, RowBounds); else g.FillRectangle(RowAlternatingBackBrush, RowBounds); // Printing each visible cell of the CurrentRow for (int CurrentCell = (int)mColumnPoints[mColumnPoint].GetValue(0); CurrentCell < (int)mColumnPoints[mColumnPoint].GetValue(1); CurrentCell++) ...{ if (!TheDataGridView.Columns[CurrentCell].Visible) continue; // If the cell is belong to invisible column, then ignore this iteration // Check the CurrentCell alignment and apply it to the CellFormat if (TheDataGridView.Columns[CurrentCell].DefaultCellStyle.Alignment.ToString().Contains("Right")) CellFormat.Alignment = StringAlignment.Far; elseif (TheDataGridView.Columns[CurrentCell].DefaultCellStyle.Alignment.ToString().Contains("Center")) CellFormat.Alignment = StringAlignment.Center; else CellFormat.Alignment = StringAlignment.Near; ColumnWidth = ColumnsWidth[CurrentCell]; RectangleF CellBounds =new RectangleF(CurrentX, CurrentY, ColumnWidth, RowsHeight[CurrentRow]); // Printing the cell text g.DrawString(TheDataGridView.Rows[CurrentRow].Cells[CurrentCell].EditedFormattedValue.ToString(), RowFont, RowForeBrush, CellBounds, CellFormat); // Drawing the cell bounds if (TheDataGridView.CellBorderStyle != DataGridViewCellBorderStyle.None) // Draw the cell border only if the CellBorderStyle is not None g.DrawRectangle(TheLinePen, CurrentX, CurrentY, ColumnWidth, RowsHeight[CurrentRow]); CurrentX += ColumnWidth; } CurrentY += RowsHeight[CurrentRow]; /**//* Checking if the CurrentY is exceeds the page boundries * If so then exit the function and returning true meaning another PagePrint action is required */ if ((int)CurrentY > (PageHeight - TopMargin - BottomMargin)) ...{ CurrentRow++; returntrue; } } CurrentRow++; } CurrentRow =0; // Continue to print the next group of columns mColumnPoint++; // Which means all columns are printed if (mColumnPoint == mColumnPoints.Count) ...{ mColumnPoint =0; returnfalse; } else returntrue; } /**////<summary> /// The method that calls all other functions ///</summary> ///<param name="g"></param> ///<returns>bool</returns> publicbool DrawDataGridView(Graphics g) ...{ try ...{ Calculate(g); DrawHeader(g); bool bContinue = DrawRows(g); return bContinue; } catch (Exception ex) ...{ MessageBox.Show("Operation failed: "+ ex.Message.ToString(), Application.ProductName +" - Error", MessageBoxButtons.OK, MessageBoxIcon.Error); returnfalse; } } }