布局打印

using System; using System.Text; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Drawing.Printing; using System.Data; using System.Globalization; using System.Windows.Forms; [module:CLSCompliant(true)] namespace DXGXGIS //AllocationRequest { /// <summary> /// Data Grid View Printer. Print functions for a datagridview, since MS /// didn't see fit to do it. /// </summary> class DGVPrinter { //--------------------------------------------------------------------- // internal classes/structs //--------------------------------------------------------------------- // handle wide-column printing - that is, lists of columns that extend // wider than one page width. Columns are broken up into "Page Sets" that // are printed one after another until all columns are printed. class PageDef { public PageDef(Margins m, int count) { colstoprint = new List<object>(count); colwidths = new List<float>(count); colwidthsoverride = new List<float>(count); coltotalwidth = 0; margins = (Margins)m.Clone(); } public IList colstoprint; public List<float> colwidths; public List<float> colwidthsoverride; public float coltotalwidth; public Margins margins; } IList<PageDef> pagesets; int currentpageset = 0; //--------------------------------------------------------------------- // global variables //--------------------------------------------------------------------- #region global variables // the data grid view we're printing DataGridView dgv = null; // print document PrintDocument printDoc = null; // print status items IList rowstoprint; IList colstoprint; // divided into pagesets for printing int lastrowprinted = -1; int fromPage = 0; int toPage = -1; // page formatting options int pageHeight = 0; int pageWidth = 0; int printWidth = 0; float rowheaderwidth = 0; int CurrentPage=0; PrintRange printRange; // calculated values private float headerHeight = 0; private float footerHeight = 0; private float pagenumberHeight = 0; private float colheaderheight = 0; private List<float> rowheights; private List<float> colwidths; #endregion //--------------------------------------------------------------------- // properties - settable by user //--------------------------------------------------------------------- #region properties // Title #region title properties // override flag bool overridetitleformat = false; /// <summary> /// Title for this report. Default is empty. /// </summary> private String title; public String Title { get { return title; } set { title = value; printDoc.DocumentName = title; } } /// <summary> /// Font for the title. Default is Tahoma, 18pt. /// </summary> private Font titlefont; public Font TitleFont { get { return titlefont; } set { titlefont = value; } } /// <summary> /// Foreground color for the title. Default is Black /// </summary> private Color titlecolor; public Color TitleColor { get { return titlecolor; } set { titlecolor = value; } } /// <summary> /// Allow the user to override the title string alignment. Default value is /// Alignment - Near; /// </summary> private StringAlignment titlealignment; public StringAlignment TitleAlignment { get { return titlealignment; } set { titlealignment = value; overridetitleformat = true; } } /// <summary> /// Allow the user to override the title string format flags. Default values /// are: FormatFlags - NoWrap, LineLimit, NoClip /// </summary> private StringFormatFlags titleformatflags; public StringFormatFlags TitleFormatFlags { get { return titleformatflags; } set { titleformatflags = value; overridetitleformat = true; } } #endregion // SubTitle #region subtitle properties // override flat bool overridesubtitleformat = false; /// <summary> /// SubTitle for this report. Default is empty. /// </summary> private String subtitle; public String SubTitle { get { return subtitle; } set { subtitle = value; } } /// <summary> /// Font for the subtitle. Default is Tahoma, 12pt. /// </summary> private Font subtitlefont; public Font SubTitleFont { get { return subtitlefont; } set { subtitlefont = value; } } /// <summary> /// Foreground color for the subtitle. Default is Black /// </summary> private Color subtitlecolor; public Color SubTitleColor { get { return subtitlecolor; } set { subtitlecolor = value; } } /// <summary> /// Allow the user to override the subtitle string alignment. Default value is /// Alignment - Near; /// </summary> private StringAlignment subtitlealignment; public StringAlignment SubTitleAlignment { get { return subtitlealignment; } set { subtitlealignment = value; overridesubtitleformat = true; } } /// <summary> /// Allow the user to override the subtitle string format flags. Default values /// are: FormatFlags - NoWrap, LineLimit, NoClip /// </summary> private StringFormatFlags subtitleformatflags; public StringFormatFlags SubTitleFormatFlags { get { return subtitleformatflags; } set { subtitleformatflags = value; overridesubtitleformat = true; } } #endregion // Footer #region footer properties // override flag bool overridefooterformat = false; /// <summary> /// footer for this report. Default is empty. /// </summary> private String footer; public String Footer { get { return footer; } set { footer = value; } } /// <summary> /// Font for the footer. Default is Tahoma, 10pt. /// </summary> private Font footerfont; public Font FooterFont { get { return footerfont; } set { footerfont = value; } } /// <summary> /// Foreground color for the footer. Default is Black /// </summary> private Color footercolor; public Color FooterColor { get { return footercolor; } set { footercolor = value; } } /// <summary> /// Allow the user to override the footer string alignment. Default value is /// Alignment - Center; /// </summary> private StringAlignment footeralignment; public StringAlignment FooterAlignment { get { return footeralignment; } set { footeralignment = value; overridefooterformat = true; } } /// <summary> /// Allow the user to override the footer string format flags. Default values /// are: FormatFlags - NoWrap, LineLimit, NoClip /// </summary> private StringFormatFlags footerformatflags; public StringFormatFlags FooterFormatFlags { get { return footerformatflags; } set { footerformatflags = value; overridefooterformat = true; } } private float footerspacing; public float FooterSpacing { get { return footerspacing; } set { footerspacing = value; } } #endregion // Page Numbering #region page number properties // override flag bool overridepagenumberformat = false; /// <summary> /// Include page number in the printout. Default is true. /// </summary> private bool pageno = true; public bool PageNumbers { get { return pageno; } set { pageno = value; } } /// <summary> /// Font for the page number, Default is Tahoma, 8pt. /// </summary> private Font pagenofont; public Font PageNumberFont { get { return pagenofont; } set { pagenofont = value; } } /// <summary> /// Text color (foreground) for the page number. Default is Black /// </summary> private Color pagenocolor; public Color PageNumberColor { get { return pagenocolor; } set { pagenocolor = value; } } /// <summary> /// Allow the user to override the page number string alignment. Default value is /// Alignment - Near; /// </summary> private StringAlignment pagenumberalignment; public StringAlignment PageNumberAlignment { get { return pagenumberalignment; } set { pagenumberalignment = value; overridepagenumberformat = true;} } /// <summary> /// Allow the user to override the pagenumber string format flags. Default values /// are: FormatFlags - NoWrap, LineLimit, NoClip /// </summary> private StringFormatFlags pagenumberformatflags; public StringFormatFlags PageNumberFormatFlags { get { return pagenumberformatflags; } set { pagenumberformatflags = value; overridepagenumberformat = true; } } /// <summary> /// Allow the user to select whether to have the page number at the top or bottom /// of the page. Default is false: page numbers on the bottom of the page /// </summary> private bool pagenumberontop = false; public bool PageNumberInHeader { get { return pagenumberontop; } set { pagenumberontop = value; } } /// <summary> /// Should the page number be printed on a separate line, or printed on the /// same line as the header / footer? Default is false; /// </summary> private bool pagenumberonseparateline = false; public bool PageNumberOnSeparateLine { get { return pagenumberonseparateline; } set { pagenumberonseparateline = value; } } #endregion // Header Cell Printing #region header cell properties // override flag bool overridecellheaderformat = false; /// <summary> /// Allow the user to override the header cell string alignment. Default value is /// Alignment - Near; /// </summary> private StringAlignment headercellalignment; public StringAlignment HeaderCellAlignment { get { return headercellalignment; } set { headercellalignment = value; overridecellheaderformat = true; } } /// <summary> /// Allow the user to override the header cell string format flags. Default values /// are: FormatFlags - NoWrap, LineLimit, NoClip /// </summary> private StringFormatFlags headercellformatflags; public StringFormatFlags HeaderCellFormatFlags { get { return headercellformatflags; } set { headercellformatflags = value; overridecellheaderformat = true; } } #endregion // Individual Cell Printing #region cell properties bool overridecellformat = false; /// <summary> /// Allow the user to override the cell string alignment. Default value is /// Alignment - Near; /// </summary> private StringAlignment cellalignment; public StringAlignment CellAlignment { get { return cellalignment; } set { cellalignment = value; overridecellformat = true; } } /// <summary> /// Allow the user to override the cell string format flags. Default values /// are: FormatFlags - NoWrap, LineLimit, NoClip /// </summary> private StringFormatFlags cellformatflags; public StringFormatFlags CellFormatFlags { get { return cellformatflags; } set { cellformatflags = value; overridecellformat = true; } } /// <summary> /// allow the user to override the column width calcs with their own defaults /// </summary> private List<float> colwidthsoverride = new List<float>(); private Dictionary<string, float> publicwidthoverrides = new Dictionary<string, float>(); public Dictionary<string, float> ColumnWidths { get { return publicwidthoverrides; } } #endregion // Page Level Properties #region page level properties /// <summary> /// Page margins override. Default is (60, 60, 40, 40) /// </summary> private Margins printmargins; public Margins PrintMargins { get { return printmargins; } set { printmargins = value; } } /// <summary> /// Spread the columns porportionally accross the page. Default is false. /// </summary> private bool porportionalcolumns = false; public bool PorportionalColumns { get { return porportionalcolumns; } set { porportionalcolumns = value; } } /// <summary> /// Center the table on the page. /// </summary> public enum Alignment { NotSet, Left, Right, Center } private Alignment tablealignment = Alignment.NotSet; public Alignment TableAlignment { get { return tablealignment; } set { tablealignment = value; } } #endregion #endregion /// <summary> /// Constructor for DGVPrinter /// </summary> public DGVPrinter () { // create print document printDoc = new PrintDocument(); printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage); printmargins = new Margins(60, 60, 40, 40); // set default fonts pagenofont = new Font("Tahoma", 8, FontStyle.Regular, GraphicsUnit.Point); pagenocolor = Color.Black; titlefont = new Font("Tahoma", 18, FontStyle.Bold, GraphicsUnit.Point); titlecolor = Color.Black; subtitlefont = new Font("Tahoma", 12, FontStyle.Bold, GraphicsUnit.Point); subtitlecolor = Color.Black; footerfont = new Font("Tahoma", 10, FontStyle.Bold, GraphicsUnit.Point); footercolor = Color.Black; // set default string formats titlealignment = StringAlignment.Center; titleformatflags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; subtitlealignment = StringAlignment.Center; subtitleformatflags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; footeralignment = StringAlignment.Center; footerformatflags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; pagenumberalignment = StringAlignment.Far; pagenumberformatflags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; headercellalignment = StringAlignment.Near; headercellformatflags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; cellalignment = StringAlignment.Near; cellformatflags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip; } /// <summary> /// Start the printing process, print to a printer. /// </summary> /// <param name="dgv">The DataGridView to print</param> /// NOTE: Any changes to this method also need to be done in PrintPreviewDataGridView public void PrintDataGridView(DataGridView dgv) { // save the datagridview we're printing this.dgv = dgv; // create new print dialog PrintDialog pd = new PrintDialog(); pd.Document = printDoc; printDoc.DefaultPageSettings.Margins = printmargins; pd.AllowCurrentPage = true; pd.AllowSelection = true; pd.AllowSomePages = true; pd.AllowPrintToFile = false; // show print dialog if (DialogResult.OK == pd.ShowDialog()) { SetupPrint(pd); printDoc.Print(); } } /// <summary> /// Start the printing process, print to a print preview dialog /// </summary> /// <param name="dgv">The DataGridView to print</param> /// NOTE: Any changes to this method also need to be done in PrintDataGridView public void PrintPreviewDataGridView(DataGridView dgv) { // save the datagridview we're printing this.dgv = dgv; // create new print dialog and set options PrintDialog pd = new PrintDialog(); pd.Document = printDoc; printDoc.DefaultPageSettings.Margins = printmargins; pd.AllowSelection = true; pd.AllowSomePages = true; pd.AllowCurrentPage = true; pd.AllowPrintToFile = false; // show print dialog if (DialogResult.OK == pd.ShowDialog()) { SetupPrint(pd); PrintPreviewDialog ppdialog = new PrintPreviewDialog(); ppdialog.Document = printDoc; ppdialog.ShowDialog(); } } /// <summary> /// Set up the print job. Save information from print dialog /// and print document for easy access. Also sets up the rows /// and columns that will be printed. At this point, we're /// collecting all columns in colstoprint. This will be broken /// up into pagesets later on /// </summary> /// <param name="pd">The print dialog the user just filled out</param> void SetupPrint(PrintDialog pd) { //----------------------------------------------------------------- // save data from print dialog and document //----------------------------------------------------------------- // check to see if we're doing landscape printing if (printDoc.DefaultPageSettings.Landscape) { // landscape: switch width and height pageHeight = printDoc.DefaultPageSettings.PaperSize.Width; pageWidth = printDoc.DefaultPageSettings.PaperSize.Height; } else { // portrait: keep width and height as expected pageHeight = printDoc.DefaultPageSettings.PaperSize.Height; pageWidth = printDoc.DefaultPageSettings.PaperSize.Width; } // Default printer margins and calc default print width printmargins = printDoc.DefaultPageSettings.Margins; printWidth = pageWidth - printmargins.Left - printmargins.Right; // save print range printRange = pd.PrinterSettings.PrintRange; // pages to print handles "some pages" option if (PrintRange.SomePages == printRange) { // set limits to only print some pages fromPage = pd.PrinterSettings.FromPage; toPage = pd.PrinterSettings.ToPage; } else { // set extremes so that we'll print all pages fromPage = 0; toPage = 2147483647; } //----------------------------------------------------------------- // set up the rows and columns to print //----------------------------------------------------------------- // rows to print (handles "selection" and "current page" options if (PrintRange.Selection == printRange) { // if DGV has rows selected, it's easy, selected rows and all visible columns if (0 != dgv.SelectedRows.Count) { rowstoprint = dgv.SelectedRows; colstoprint = new List<object>(dgv.Columns.Count); foreach (DataGridViewColumn col in dgv.Columns) if (col.Visible) colstoprint.Add(col); } // if selected columns, then all rows, and selected columns else if (0 != dgv.SelectedColumns.Count) { rowstoprint = dgv.Rows; colstoprint = dgv.SelectedColumns; } // we just have a bunch of selected cells so we have to do some work else { // set up sorted lists. the selectedcells method does not guarantee // that the cells will always be in left-right top-bottom order. SortedList temprowstoprint = new SortedList(dgv.SelectedCells.Count); SortedList tempcolstoprint = new SortedList(dgv.SelectedCells.Count); // for each selected cell, add unique rows and columns int colindex, rowindex; foreach (DataGridViewCell cell in dgv.SelectedCells) { colindex = cell.ColumnIndex; rowindex = cell.RowIndex; // add unique rows if (!temprowstoprint.Contains(rowindex)) temprowstoprint.Add(rowindex, dgv.Rows[rowindex]); // add unique columns if (!tempcolstoprint.Contains(colindex)) tempcolstoprint.Add(colindex, dgv.Columns[colindex]); } // Move the now-duplicate free columns and rows to our list of what to print rowstoprint = new List<object>(temprowstoprint.Count); foreach (object item in temprowstoprint.Values) rowstoprint.Add(item); colstoprint = new List<object>(tempcolstoprint.Count); foreach (object item in tempcolstoprint.Values) colstoprint.Add(item); } } // if current page was selected, print visible columns for the // displayed rows else if (PrintRange.CurrentPage == printRange) { // create lists rowstoprint = new List<object>(dgv.DisplayedRowCount(true)); colstoprint = new List<object>(dgv.Columns.Count); // select all visible rows on displayed page for (int i = dgv.FirstDisplayedScrollingRowIndex; i < dgv.FirstDisplayedScrollingRowIndex + dgv.DisplayedRowCount(true); i++) { DataGridViewRow row = dgv.Rows[i]; if (row.Visible) rowstoprint.Add(row); } // select all visible columns colstoprint = new List<object>(dgv.Columns.Count); foreach (DataGridViewColumn col in dgv.Columns) if (col.Visible) colstoprint.Add(col); } // this is the default for print all - everything marked visible will be printed else { // select all visible rows and all visible columns rowstoprint = new List<object>(dgv.Rows.Count); foreach (DataGridViewRow row in dgv.Rows) if (row.Visible) rowstoprint.Add(row); colstoprint = new List<object>(dgv.Columns.Count); foreach (DataGridViewColumn col in dgv.Columns) if (col.Visible) colstoprint.Add(col); } // Reorder columns based on Display Index (if the programmer or user has // changed the column display order we want to respect it in the printout) SortedList displayorderlist = new SortedList(colstoprint.Count); foreach (DataGridViewColumn col in colstoprint) displayorderlist.Add(col.DisplayIndex, col); colstoprint.Clear(); foreach (object item in displayorderlist.Values) colstoprint.Add(item); // Adjust override list to have the same number of entries as colstoprint foreach (DataGridViewColumn col in colstoprint) if (publicwidthoverrides.ContainsKey(col.Name)) colwidthsoverride.Add(publicwidthoverrides[col.Name]); else colwidthsoverride.Add(-1); //for (int i = colwidthsoverride.Count; i < colstoprint.Count; i++) // colwidthsoverride.Add(-1); // Measure the print area measureprintarea(printDoc.PrinterSettings.CreateMeasurementGraphics()); } /// <summary> /// Centralize the string format settings. Does the work of checking for user /// overrides, and if they're not present, setting the cell alignment to match /// (somewhat) the source control's string alignment. /// </summary> /// <param name="alignment">String alignment</param> /// <param name="flags">String format flags</param> /// <param name="controlstyle">DataGridView style to apply (if available)</param> /// <param name="overrideformat">True if user overrode alignment or flags</param> /// <returns></returns> private static StringFormat managestringformat(StringAlignment alignment, StringFormatFlags flags, DataGridViewCellStyle controlstyle, bool overrideformat) { // start with the provided StringFormat format = new StringFormat(); format.Trimming = StringTrimming.Word; format.Alignment = alignment; format.FormatFlags = flags; format.Trimming = StringTrimming.Character; // if the format was overridded by the user, make no adjustments if (!overrideformat) { // check to see if a style is provided if (null != controlstyle) { // Adjust the format based on the control settings, bias towards centered DataGridViewContentAlignment cellalign = controlstyle.Alignment; if (cellalign.ToString().Contains("Center")) format.Alignment = StringAlignment.Center; else if (cellalign.ToString().Contains("Left")) format.Alignment = StringAlignment.Near; else if (cellalign.ToString().Contains("Right")) format.Alignment = StringAlignment.Far; } } return format; } /// <summary> /// Scan all the rows and columns to be printed and calculate the /// overall individual column width (based on largest column value), /// the header sizes, and determine all the row heights. /// </summary> /// <param name="g">The graphics context for all measurements</param> private void measureprintarea(Graphics g) { int i, j; rowheights = new List<float>(rowstoprint.Count); colwidths = new List<float>(colstoprint.Count); headerHeight = 0; footerHeight = 0; // temp variables DataGridViewColumn col; DataGridViewRow row; //----------------------------------------------------------------- // measure the page headers and footers, including the grid column header cells //----------------------------------------------------------------- // measure the column headers Font headerfont = dgv.ColumnHeadersDefaultCellStyle.Font; if (null == headerfont) headerfont = dgv.DefaultCellStyle.Font; // set initial column sizes based on column titles for (i = 0; i < colstoprint.Count; i++) { col = (DataGridViewColumn)colstoprint[i]; // measure the title for each column, keep widths and biggest height SizeF size = g.MeasureString(col.HeaderText, headerfont); colwidths.Add(size.Width); colheaderheight = (colheaderheight < size.Height ? size.Height : colheaderheight); } //----------------------------------------------------------------- // measure the page number //----------------------------------------------------------------- if (pageno) { StringFormat format = managestringformat(pagenumberalignment, pagenumberformatflags, dgv.DefaultCellStyle, overridetitleformat); pagenumberHeight = (g.MeasureString("Page", pagenofont, printWidth, format)).Height; } //----------------------------------------------------------------- // Calc height of header. // Header height is height of page number, title, subtitle and height of column headers //----------------------------------------------------------------- // note that we dont count the page number height if it's not on a separate line if (pagenumberontop && !pagenumberonseparateline) { headerHeight += pagenumberHeight; } if (!String.IsNullOrEmpty(title)) { StringFormat format = managestringformat(titlealignment, titleformatflags, null, overridetitleformat); headerHeight += (g.MeasureString(title, titlefont, printWidth, format)).Height; } if (!String.IsNullOrEmpty(subtitle)) { StringFormat format = managestringformat(subtitlealignment, subtitleformatflags, null, overridetitleformat); headerHeight += (g.MeasureString(subtitle, subtitlefont, printWidth, format)).Height; } headerHeight += colheaderheight; //----------------------------------------------------------------- // measure the footer, if one is provided. Include the page number if we're printing // it on the bottom //----------------------------------------------------------------- if (!String.IsNullOrEmpty(footer)) { StringFormat format = managestringformat(footeralignment, footerformatflags, null, overridefooterformat); footerHeight += (g.MeasureString(footer, footerfont, printWidth, format)).Height; } // note we don't count the page number height if it's not on a separate line if (!pagenumberontop && !pagenumberonseparateline) { footerHeight += pagenumberHeight; } footerHeight += footerspacing; //----------------------------------------------------------------- // measure the grid to be printed ... this gets us all the row heights // and an accurate measure of column widths for the printed area //----------------------------------------------------------------- Font defaultfont = dgv.DefaultCellStyle.Font; Font cellfont; for (i = 0; i < rowstoprint.Count; i++) { row = (DataGridViewRow) rowstoprint[i]; rowheights.Add(0); // add row headers if they're visible if (dgv.RowHeadersVisible) { SizeF rhsize = g.MeasureString(row.HeaderCell.EditedFormattedValue.ToString(), headerfont); rowheaderwidth = (rowheaderwidth < rhsize.Width) ? rhsize.Width : rowheaderwidth; } // calculate widths for each column. We're looking for the largest width needed for // all the rows of data. for (j = 0; j < colstoprint.Count; j++) { col = (DataGridViewColumn) colstoprint[j]; if (row.Cells[col.Name].HasStyle && (null != row.Cells[col.Name].Style.Font)) cellfont = row.Cells[col.Name].Style.Font; else cellfont = defaultfont; // get the raw size of the string. SizeF size = g.MeasureString(row.Cells[col.Name].EditedFormattedValue.ToString(), cellfont); // Handle fixed size cells and > printwidth cells where the width of the // data won't fit. (I.E. need to stretch the row down the page) if ((0 < colwidthsoverride[j]) || (size.Width > printWidth)) { // set column width if (0 < colwidthsoverride[j]) colwidths[j] = colwidthsoverride[j]; else if (size.Width > printWidth) colwidths[j] = printWidth; // remeasure the string with the new limits and proper formatting for wrapping. // Use an absurd height value so that we can get the real number of lines printed int chars, lines; StringFormat format = managestringformat(cellalignment, cellformatflags, col.InheritedStyle, overridecellformat); g.MeasureString(row.Cells[col.Name].EditedFormattedValue.ToString(), cellfont, new SizeF(colwidths[j], 2147483647), format, out chars, out lines); // set row height float tempheight = lines * size.Height; rowheights[i] = (rowheights[i] < tempheight ? tempheight : rowheights[i]); } else { colwidths[j] = (colwidths[j] < size.Width ? size.Width : colwidths[j]); rowheights[i] = (rowheights[i] < size.Height ? size.Height : rowheights[i]); } } } //----------------------------------------------------------------- // Break the columns accross page sets. This is the key to printing // where the total width is wider than one page. //----------------------------------------------------------------- // assume everything will fit on one page pagesets = new List<PageDef>(); pagesets.Add(new PageDef(printmargins, colstoprint.Count)); int pset = 0; // Account for row headers pagesets[pset].coltotalwidth = rowheaderwidth; // split columns into page sets float columnwidth; for (i = 0; i < colstoprint.Count; i++) { // get initial column width columnwidth = (colwidthsoverride[i] >= 0) ? colwidthsoverride[i] : colwidths[i]; // See if the column width takes us off the page - Except for the // first column. This will prevent printing an empty page!! Otherwise, // columns longer than the page width are printed on their own page if (printWidth < (pagesets[pset].coltotalwidth + columnwidth) && i != 0) { pagesets.Add(new PageDef(printmargins, colstoprint.Count)); pset++; // Account for row headers pagesets[pset].coltotalwidth = rowheaderwidth; } // update page set definition pagesets[pset].colstoprint.Add(colstoprint[i]); pagesets[pset].colwidths.Add(colwidths[i]); pagesets[pset].colwidthsoverride.Add(colwidthsoverride[i]); pagesets[pset].coltotalwidth += columnwidth; } //----------------------------------------------------------------- // Adjust column widths and table margins for each page //----------------------------------------------------------------- for (i=0; i<pagesets.Count; i++) AdjustPageSets(g, pagesets[i]); } /// <summary> /// Adjust column widths for fixed and porportional columns, set the /// margins to enforce the selected tablealignment. /// </summary> /// <param name="g">The graphics context for all measurements</param> /// <param name="pageset">The pageset to adjust</param> private void AdjustPageSets(Graphics g, PageDef pageset) { int i; float fixedcolwidth = rowheaderwidth; float remainingcolwidth = 0; float ratio; //----------------------------------------------------------------- // Adjust the column widths in the page set to their final values, // accounting for overridden widths and porportional column stretching //----------------------------------------------------------------- // calculate the amount of space reserved for fixed width columns for (i = 0; i < pageset.colwidthsoverride.Count; i++) if (pageset.colwidthsoverride[i] >= 0) fixedcolwidth += pageset.colwidthsoverride[i]; // calculate the amount space for non-overridden columns for (i = 0; i < pageset.colwidths.Count; i++) if (pageset.colwidthsoverride[i] < 0) remainingcolwidth += pageset.colwidths[i]; // calculate the ratio for porportional colums, use 1 for no // non-overridden columns or not porportional if (porportionalcolumns && 0 < remainingcolwidth) ratio = ((float)printWidth - fixedcolwidth) / (float)remainingcolwidth; else ratio = (float)1.0; // reset all column widths for override and/or porportionality. coltotalwidth // for each pageset should be <= pageWidth pageset.coltotalwidth = rowheaderwidth; for (i = 0; i < pageset.colwidths.Count; i++) { if (pageset.colwidthsoverride[i] >= 0) pageset.colwidths[i] = pageset.colwidthsoverride[i]; else pageset.colwidths[i] = pageset.colwidths[i] * ratio; pageset.coltotalwidth += pageset.colwidths[i]; } //----------------------------------------------------------------- // Table Alignment - now that we have the column widths established // we can reset the table margins to get left, right and centered // for the table on the page //----------------------------------------------------------------- // Reset Print Margins based on table alignment if (Alignment.Left == tablealignment) { // Bias table to the left by setting "right" value pageset.margins.Right = pageWidth - pageset.margins.Left - (int)pageset.coltotalwidth; if (0 > pageset.margins.Right) pageset.margins.Right = 0; } else if (Alignment.Right == tablealignment) { // Bias table to the right by setting "left" value pageset.margins.Left = pageWidth - pageset.margins.Right - (int)pageset.coltotalwidth; if (0 > pageset.margins.Left) pageset.margins.Left = 0; } else if (Alignment.Center == tablealignment) { // Bias the table to the center by setting left and right equal pageset.margins.Left = (pageWidth - (int)pageset.coltotalwidth) / 2; if (0 > pageset.margins.Left) pageset.margins.Left = 0; pageset.margins.Right = pageset.margins.Left; } } /// <summary> /// Check for more pages. This is called at the end of printing a page set. /// If there's another page set to print, we return true. /// </summary> private bool HasMorePages() { currentpageset++; if (currentpageset < pagesets.Count) return true; else return false; } /// <summary> /// PrintPage event handler. This routine prints one page. It will /// skip non-printable pages if the user selected the "some pages" option /// on the print dialog. /// </summary> /// <param name="sender">default object from windows</param> /// <param name="e">Event info from Windows about the printing</param> private void printDoc_PrintPage(object sender, PrintPageEventArgs e) { // flag for handling printing some pages rather than all bool printthispage = false; // current printing position within one page float printpos = printmargins.Top; // increment page number & check page range CurrentPage++; if ((CurrentPage >= fromPage) && (CurrentPage <= toPage)) printthispage = true; // calculate the static vertical space used float staticheight = pageHeight - footerHeight - printmargins.Top - printmargins.Bottom; // holder for one-row height lookahead to see if the row will fit on the page float nextrowheight; //----------------------------------------------------------------- // scan down heights until we're off this (non-printing) page //----------------------------------------------------------------- while (!printthispage) { // calculate and increment over the page we're not printing printpos = printmargins.Top + headerHeight; // do one row look-ahead to see if we have room on the page nextrowheight = (lastrowprinted < rowheights.Count) ? rowheights[lastrowprinted + 1] : 0; while ((printpos + nextrowheight) < staticheight) { lastrowprinted++; printpos += rowheights[lastrowprinted]; nextrowheight = (lastrowprinted < rowheights.Count) ? rowheights[lastrowprinted + 1] : 0; } // skip to the next page & see if it's in the print range CurrentPage++; if ((CurrentPage >= fromPage) && (CurrentPage <= toPage)) printthispage = true; // bottom check~ if ((lastrowprinted >= rowstoprint.Count-1) || (CurrentPage > toPage)) { // we're done here - nothing more to print e.HasMorePages = HasMorePages(); return; } } //----------------------------------------------------------------- // print headers //----------------------------------------------------------------- // reset printpos as it may have changed during the 'skip pages' routine just above. printpos = printmargins.Top; // print page number if user selected it if (pagenumberontop) { // if we have a page number to print if (pageno) { String pagenumber = "Page " + CurrentPage.ToString(CultureInfo.CurrentCulture); if (1 < pagesets.Count) pagenumber += " - Part " + (currentpageset + 1).ToString(CultureInfo.CurrentCulture); // ... then print it printsection(e.Graphics, ref printpos, pagenumber, pagenofont, pagenocolor, pagenumberalignment, pagenumberformatflags, overridepagenumberformat, printmargins); // if the page number is not on a separate line, don't "use up" it's vertical space if (!pagenumberonseparateline) printpos -= pagenumberHeight; } } // print title if provided if (!String.IsNullOrEmpty(title)) printsection(e.Graphics, ref printpos, title, titlefont, titlecolor, titlealignment, titleformatflags, overridetitleformat, printmargins); // print subtitle if provided if (!String.IsNullOrEmpty(subtitle)) printsection(e.Graphics, ref printpos, subtitle, subtitlefont, subtitlecolor, subtitlealignment, subtitleformatflags, overridesubtitleformat, printmargins); // print column headers printcolumnheaders(e.Graphics, ref printpos, pagesets[currentpageset]); //----------------------------------------------------------------- // print rows until the page is complete //----------------------------------------------------------------- // do one row look-ahead to see if we have room on the page nextrowheight = (lastrowprinted < rowheights.Count) ? rowheights[lastrowprinted + 1] : 0; while ((printpos + nextrowheight) < staticheight) { lastrowprinted++; printrow(e.Graphics, ref printpos, (DataGridViewRow)(rowstoprint[lastrowprinted]), pagesets[currentpageset]); // bottom check, we're really really done when there's no more data if (lastrowprinted >= rowstoprint.Count - 1) { // check on more page sets or set no more pages flag e.HasMorePages = HasMorePages(); // print a footer for this page printfooter(e.Graphics, ref printpos, printmargins); // reset counters since we'll go through this twice if we print from preview lastrowprinted = -1; CurrentPage = 0; // return return; } else nextrowheight = (lastrowprinted < rowheights.Count) ? rowheights[lastrowprinted + 1] : 0; } //----------------------------------------------------------------- // print footer //----------------------------------------------------------------- printfooter(e.Graphics, ref printpos, printmargins); //----------------------------------------------------------------- // bottom check, see if this is the last page to print //----------------------------------------------------------------- if (CurrentPage >= toPage) { // reset for next pageset or tell the caller we're complete e.HasMorePages = HasMorePages(); // reset counters since we'll go through this twice if we print from preview lastrowprinted = -1; CurrentPage = 0; } else { // we're not done yet e.HasMorePages = true; } return; } /// <summary> /// Print a header or footer section. Used for page numbers and titles /// </summary> /// <param name="g">Graphic context to print in</param> /// <param name="pos">Track vertical space used; 'y' location</param> /// <param name="text">String to print</param> /// <param name="font">Font to use for printing</param> /// <param name="color">Color to print in</param> /// <param name="alignment">Alignment - print to left, center or right</param> /// <param name="flags">String format flags</param> /// <param name="useroverride">True if the user overrode the alignment or flags</param> /// <param name="margins">The table's print margins</param> private void printsection(Graphics g, ref float pos, string text, Font font, Color color, StringAlignment alignment, StringFormatFlags flags, bool useroverride, Margins margins) { // string formatting setup StringFormat printformat = managestringformat(alignment, flags, null, useroverride); // measure string SizeF printsize = g.MeasureString(text, font, printWidth, printformat); // build area to print within RectangleF printarea = new RectangleF((float)margins.Left, pos, (float)printWidth, printsize.Height); // do the actual print g.DrawString(text, font, new SolidBrush(color), printarea, printformat); // track "used" vertical space pos += printsize.Height; } /// <summary> /// Print the footer. This handles the footer spacing, and printing the page number /// at the bottom of the page (if the page number is not in the header). /// </summary> /// <param name="g">Graphic context to print in</param> /// <param name="pos">Track vertical space used; 'y' location</param> /// <param name="margins">The table's print margins</param> private void printfooter(Graphics g, ref float pos, Margins margins) { // print last footer. Note: need to force printpos to the bottom of the page // as we may have run out of data anywhere on the page pos = pageHeight - footerHeight - margins.Top - margins.Bottom; // add spacing pos += footerspacing; // print the footer printsection(g, ref pos, footer, footerfont, footercolor, footeralignment, footerformatflags, overridefooterformat, margins); // print the page number if it's on the bottom. if (!pagenumberontop) { if (pageno) { String pagenumber = "Page " + CurrentPage.ToString(CultureInfo.CurrentCulture); if (1 < pagesets.Count) pagenumber += " - Part " + (currentpageset+1).ToString(CultureInfo.CurrentCulture); // if the pageno is not on a separate line, push the print location up by its height. if (!pagenumberonseparateline) pos = pos - pagenumberHeight; // print the page number printsection(g, ref pos, pagenumber, pagenofont, pagenocolor, pagenumberalignment, pagenumberformatflags, overridepagenumberformat, margins); } } } /// <summary> /// Print the column headers. Most printing format info is retrieved from the /// source DataGridView. /// </summary> /// <param name="g">Graphics Context to print within</param> /// <param name="pos">Track vertical space used; 'y' location</param> /// <param name="pageset">Current pageset - defines columns and margins</param> private void printcolumnheaders(Graphics g, ref float pos, PageDef pageset) { // track printing location accross the page. start position is hard left, // adjusted for the row headers. Note rowheaderwidth is 0 if row headers are not printed float xcoord = pageset.margins.Left + rowheaderwidth; // set the pen for drawing the grid lines Pen lines = new Pen(dgv.GridColor, 1); //----------------------------------------------------------------- // Print the column headers //----------------------------------------------------------------- DataGridViewColumn col; for (int i = 0; i < pageset.colstoprint.Count; i++) { col = (DataGridViewColumn)pageset.colstoprint[i]; // calc cell width, account for columns larger than the print area! float cellwidth = (pageset.colwidths[i] > printWidth-rowheaderwidth ? printWidth-rowheaderwidth : pageset.colwidths[i]); // get column style DataGridViewCellStyle style = col.HeaderCell.InheritedStyle; // set print area for this individual cell, account for cells larger // than the print area! RectangleF cellprintarea = new RectangleF(xcoord, pos, cellwidth, colheaderheight); // print column header background g.FillRectangle(new SolidBrush(style.BackColor), cellprintarea); // set individual column format if it's provided StringFormat colformat = managestringformat(headercellalignment, headercellformatflags, style, overridecellheaderformat); // draw the text g.DrawString(col.HeaderText, style.Font, new SolidBrush(style.ForeColor), cellprintarea, colformat); // draw the borders - default to the dgv's border setting; account for // columns larger than the print width if (dgv.ColumnHeadersBorderStyle != DataGridViewHeaderBorderStyle.None) g.DrawRectangle(lines, xcoord, pos, cellwidth, colheaderheight); xcoord += pageset.colwidths[i]; } // all done, consume "used" vertical space, including space for border lines pos += colheaderheight + (dgv.ColumnHeadersBorderStyle != DataGridViewHeaderBorderStyle.None ? lines.Width : 0); } /// <summary> /// Print one row of the DataGridView. Most printing format info is retrieved /// from the DataGridView. /// </summary> /// <param name="g">Graphics Context to print within</param> /// <param name="pos">Track vertical space used; 'y' location</param> /// <param name="row">The row that will be printed</param> /// <param name="pageset">Current Pageset - defines columns and margins</param> private void printrow(Graphics g, ref float pos, DataGridViewRow row, PageDef pageset) { // track printing location accross the page float xcoord = pageset.margins.Left; // set the pen for drawing the grid lines Pen lines = new Pen(dgv.GridColor, 1); //----------------------------------------------------------------- // Print Row background //----------------------------------------------------------------- // get current row style, start with header style DataGridViewCellStyle rowstyle = row.InheritedStyle; // calc row width, account for columns larger than the print area! float rowwidth = (pageset.coltotalwidth > printWidth ? printWidth : pageset.coltotalwidth); // define print rectangle RectangleF printarea = new RectangleF(xcoord, pos, rowwidth, rowheights[lastrowprinted]); // fill in the row background g.FillRectangle(new SolidBrush(rowstyle.BackColor), printarea); //----------------------------------------------------------------- // Print the Row Headers, if they are visible //----------------------------------------------------------------- if (dgv.RowHeadersVisible) { // get current row style, start with header style DataGridViewCellStyle headerstyle = row.HeaderCell.InheritedStyle; // set print area for this individual cell RectangleF headercellprintarea = new RectangleF(xcoord, pos, rowheaderwidth, rowheights[lastrowprinted]); // fill in the row header background g.FillRectangle(new SolidBrush(headerstyle.BackColor), headercellprintarea); // set printing format for the row headers StringFormat rowheaderformat = managestringformat(headercellalignment, headercellformatflags, headerstyle, overridecellheaderformat); // draw the text for the row header cell g.DrawString(row.HeaderCell.EditedFormattedValue.ToString(), headerstyle.Font, new SolidBrush(headerstyle.ForeColor), headercellprintarea, rowheaderformat); // draw the borders - default to the dgv's border setting if (dgv.RowHeadersBorderStyle != DataGridViewHeaderBorderStyle.None) g.DrawRectangle(lines, xcoord, pos, rowheaderwidth, rowheights[lastrowprinted]); // track horizontal space used xcoord += rowheaderwidth; } //----------------------------------------------------------------- // Print the row: write and draw each cell //----------------------------------------------------------------- DataGridViewColumn col; for (int i = 0; i < pageset.colstoprint.Count; i++) { // access the column being printed col = (DataGridViewColumn)pageset.colstoprint[i]; // calc cell width, account for columns larger than the print area! float cellwidth = (pageset.colwidths[i] > printWidth - rowheaderwidth ? printWidth - rowheaderwidth : pageset.colwidths[i]); // get column style DataGridViewCellStyle colstyle = col.InheritedStyle; // set print area for this individual cell RectangleF cellprintarea = new RectangleF(xcoord, pos, cellwidth, rowheights[lastrowprinted]); // fill in the cell background - NOTE: we're using the row background because // the alternating row color settings does NOT change the cell background! // g.FillRectangle(new SolidBrush(colstyle.BackColor), cellprintarea); // set individual column format if it's provided StringFormat colformat = managestringformat(cellalignment, cellformatflags, colstyle, overridecellformat); // draw the text for the cell at the row / col intersection g.DrawString(row.Cells[col.Index].EditedFormattedValue.ToString(), colstyle.Font, new SolidBrush(colstyle.ForeColor), cellprintarea, colformat); // draw the borders - default to the dgv's border setting if (dgv.BorderStyle != BorderStyle.None) g.DrawRectangle(lines, xcoord, pos, cellwidth, rowheights[lastrowprinted]); // track horizontal space used xcoord += pageset.colwidths[i]; } //----------------------------------------------------------------- // All done with this row, consume "used" vertical space //----------------------------------------------------------------- pos += rowheights[lastrowprinted]; } } }  

你可能感兴趣的:(布局打印)