以前寫打印都是根據打印機的型號,找開發類庫。然后在此基礎上開發。寫的多了自然就想到了封裝。這是還是想到了微軟,微軟封裝了PrintDocument的打印類。但這只是在低層對串口的封裝。
所以對於應用層用着還不是很方便。最近做一個項目順便就封裝了一個調用默認打印機的類。雖說有幾個小bug,但對於目前來說,已經滿足需求了。以后不夠了在來升級吧。
1,關於打印上下左右邊距和紙張的高寬。以往都把這些寫死到代碼里面。既然是調用默認打印機,打印機的型號自然有差異。所以我就把這些配置放到app.config里面。但又怕每次打印都加載config影響效率。故此設計了個PrintPaper類。里面所有屬性都是靜態的。還有一個靜態的構造方法。這樣只有在程序開始運行加載一次config。之后就直接從內存讀取了。
PrintPaper類
1 /*CreateBy:Bonker,Date:20121115*/ 2 /// <summary> 3 /// 從配置文件讀取紙張的大小,與邊框的距離 4 /// </summary> 5 public class PrintPaper 6 { 7 /// <summary> 8 /// 距離上邊界的距離 9 /// </summary> 10 public static int MarginTop { set; get; } 11 /// <summary> 12 /// 距離左邊界的距離 13 /// </summary> 14 public static int MarginLeft { set; get; } 15 /// <summary> 16 /// 距離右邊界的距離 17 /// </summary> 18 public static int MarginRight { set; get; } 19 /// <summary> 20 /// 距離下邊界的距離 21 /// </summary> 22 public static int MarginBottom { set; get; } 23 /// <summary> 24 /// 紙張的寬度 25 /// </summary> 26 public static int Width { set; get; } 27 /// <summary> 28 /// 紙張的高度 29 /// </summary> 30 public static int Height { set; get; } 31 /// <summary> 32 /// 異常情況 33 /// </summary> 34 public static string Error { set; get; } 35 36 //對於靜態屬性和構造方法。當第一次使用該類的時候,先初始化靜態屬性,然后調用靜態類。 37 //故此配置文件只加載一次,以后調用都會從內存中讀取。 38 //此中寫法的好處,一次加載,以后不再加載,速度快。弊端:程序運行過程中,改變了config配置。則需重新運行程序。配置才加載生效。 39 static PrintPaper() 40 { 41 //先給異常賦空值,當異常不為空時。說明配置數據有問題,或者程序有異常 42 Error = null; 43 string marginTop = BonkerConfig.GetConfig("marginTop"); 44 string marginBottom = BonkerConfig.GetConfig("marginBottom"); 45 string marginLeft = BonkerConfig.GetConfig("marginLeft"); 46 string marginRight = BonkerConfig.GetConfig("marginRight"); 47 //margin的值可以為負值,但是width只能為正, 48 //marginTop,等值默認已經為0,如果margin不為空,取值復制給margin 49 try 50 { 51 if (!string.IsNullOrWhiteSpace(marginTop)) 52 { 53 MarginTop = int.Parse(marginTop); 54 } 55 if (!string.IsNullOrWhiteSpace(marginTop)) 56 { 57 MarginBottom = int.Parse(marginBottom); 58 } 59 if (!string.IsNullOrWhiteSpace(marginTop)) 60 { 61 MarginLeft = int.Parse(marginLeft); 62 } 63 if (!string.IsNullOrWhiteSpace(marginTop)) 64 { 65 MarginRight = int.Parse(marginRight); 66 } 67 } 68 catch (Exception ex) 69 { 70 //如果有異常繼續 71 Error = ex.Message; 72 return; 73 } 74 //判斷紙張寬度 75 try 76 { 77 //如果paperWidth配置不為正,則為PrintCore類打印的時候,則去默認值 78 string width = BonkerConfig.GetConfig("paperWidth"); 79 string height = BonkerConfig.GetConfig("paperWidth"); 80 if (!string.IsNullOrWhiteSpace(width)) 81 { 82 Width = int.Parse(width); 83 } 84 if (!string.IsNullOrWhiteSpace(height)) 85 { 86 Height = int.Parse(width); 87 } 88 } 89 catch (Exception ex) 90 { 91 //如果有異常繼續 92 Error = ex.Message; 93 return; 94 } 95 } 96 }
app.config的內容
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <!--******************************連接字符串設置************************************--> <add key="DBConnectionStr" value=" "/> <!--********************************打印邊界設置**********************************--> <!--打印紙的距四個邊界的距離,為空這表示默認為0,可以為負值--> <!--與上邊界距離--> <add key="marginTop" value=""/> <!--與上邊界距離--> <add key="marginBottom" value=""/> <!--與上邊界距離--> <add key="marginLeft" value=""/> <!--與上邊界距離--> <add key="marginRight" value=""/> <!--********************************打印紙大小設置**********************************--> <!--打印紙張的大小,為空表示取默認值,不可以為負值 --> <!--紙張的寬度--> <add key="paperWidth" value=""/> <!--紙張的高度--> <add key="paperHeight" value=""/> <!--*******************************************************************************--> </appSettings> </configuration>
2,打印表格,翻閱了很多msdn大神的代碼。大致有點眉目。打印表格是在一點打印完字體后,然后不改變X,Y坐標繼續打印個矩形。所以就有表格了。這樣的表格理論上表格的四個邊框有點細。里面的小格子有點粗。但打印出來后基本就沒差別了。
打印表格是自適應表格里面的文字最大的寬度。但如果表格里面確實列很少,沒列的最大寬度又很小。打印完真整個表格沒有頁面的紙張寬。那會自動拉寬每一列的寬度。
打印的核心類PrintCore
/*CreateBy:Bonker,Date:20121115*/ /// <summary> /// 打印類,負責打印表格,普通行等。 /// </summary> public class PrintCore { /// <summary> /// 打印基礎封裝類PrintDocument /// </summary> public PrintDocument printDoc { set; get; } /// <summary> /// 當前打印的x坐標 /// </summary> public float currentX { set; get; } /// <summary> /// 當前打印的Y坐標 /// </summary> public float currentY { set; get; } /// <summary> /// 默認打印字體 /// </summary> public Font defaultFont { set; get; } /// <summary> /// 打印的畫刷,默認黑色加粗 /// </summary> public Brush defaultBrush { set; get; } /// <summary> /// 是否居中打印,默認為false /// </summary> public bool isCenter { set; get; } /// <summary> /// 異常錯誤 /// </summary> public string Error { set; get; } private Graphics graphic { set; get; } /// 構造函數 /// </summary> /// <param name="_printDoc">打印基礎類</param> /// <param name="_currentX">打印開始的x坐標,默認為0</param> /// <param name="_currentY">打印開始的y坐標,默認為0</param> public PrintCore(PrintDocument _printDoc, Graphics _graphics, Font _defaultFont, float _currentX = 0, float _currentY = 0) { this.printDoc = _printDoc; this.currentX = _currentX; this.currentY = _currentY; this.defaultFont = _defaultFont; this.graphic = _graphics; this.defaultBrush = new SolidBrush(Color.Black); //默認加粗黑色 this.isCenter = false; //讀取配置文件 printDocConfig(_printDoc); Error = PrintPaper.Error; } private void printDocConfig(PrintDocument _printDoc) { _printDoc.DefaultPageSettings.Margins = new Margins(PrintPaper.MarginLeft, PrintPaper.MarginRight, PrintPaper.MarginTop, PrintPaper.MarginBottom); //當paper配置的寬度和高度都大於0時,才配置。否則忽略 if (PrintPaper.Width > 0 && PrintPaper.Height > 0) { _printDoc.DefaultPageSettings.PaperSize = new PaperSize("", PrintPaper.Width, PrintPaper.Height); } } /// <summary> /// 打印字符串,系統可以總動判斷換行打印。 /// </summary> /// <param name="prnStr">打印的字符串</param> /// <param name="isPrintLine">打印完成后,是否換行,默認為true</param> public void printString(string prnStr, bool isPrintLine = true) { //打印字符串,根據字符串長度,和紙張寬度,高度等自動換行 SizeF measure = graphic.MeasureString(prnStr, defaultFont); //如果x坐標不為0,或者打印的一行寬度大於紙張的寬度,則居中打印是沒用的。不考慮打印 if (!isCenter || currentX != 0 || printDoc.DefaultPageSettings.PaperSize.Width < measure.Width) { //計算打印這么多字要多少行 int rows = (int)Math.Ceiling(measure.Width / (printDoc.DefaultPageSettings.PaperSize.Width - currentX)); //根據行,算出要打印的邊界矩形框 graphic.DrawString(prnStr, defaultFont, defaultBrush, new Rectangle((int)currentX, (int)currentY, (int)Math.Ceiling((printDoc.DefaultPageSettings.PaperSize.Width - currentX)), (int)Math.Ceiling((measure.Height * rows)))); if (isPrintLine)//如果換行 { currentY = currentY + measure.Height * rows; currentX = 0; } else { currentY = currentY + measure.Height * (rows - 1); currentX = (measure.Width % (printDoc.DefaultPageSettings.PaperSize.Width - currentX)) + currentX; } } else { //居中打印一行 //計算打印前的留白寬度 float blank = (printDoc.DefaultPageSettings.PaperSize.Width - measure.Width) / 2.0f; currentX = currentX + blank; graphic.DrawString(prnStr, defaultFont, defaultBrush, currentX, currentY); if (isPrintLine)//如果換行 { currentX = 0; currentY = currentY + measure.Height; } else { currentX = currentX + measure.Width; } } } /// <summary> /// 打印表格,自適應沒列的寬度 /// </summary> /// <param name="prnDgv"></param> /// <param name="isPrintLine"></param> public void printDataGridView(DataGridView prnDgv, Font titleFont, Brush titleBrush, Color titleBackGroup, bool isPrintLine = true) { if (prnDgv == null) { return; } prnDgv.AllowUserToAddRows = false; //記錄每一列的寬度 int[] columnWidths = new int[prnDgv.ColumnCount]; //******************取每列的最大寬度*********************** //先計算表頭的寬度 for (int i = 0; i < prnDgv.ColumnCount; i++) { string celValue = prnDgv.Columns[i].HeaderText; SizeF measure = graphic.MeasureString(celValue, titleFont); columnWidths[i] = (int)Math.Ceiling(measure.Width);//把打印表頭所占的寬度 先放到columnWidths里面 } //計算表中數據打印的最大寬度 for (int i = 0; i < prnDgv.Rows.Count; i++) { for (int j = 0; j < prnDgv.ColumnCount; j++) { string celValue = prnDgv[j, i].Value.ToString(); SizeF measure = graphic.MeasureString(celValue, defaultFont); if (columnWidths[j] < measure.Width)//如果寬度小於打印寬度,則把長的打印寬度賦值給列寬 { columnWidths[j] = (int)Math.Ceiling(measure.Width); } } } //如果表格的寬度小於紙張的寬度,表格沒列的寬度加大 int allColumsWidth = 0; for (int i = 0; i < prnDgv.ColumnCount; i++) { allColumsWidth += columnWidths[i];//把打印表頭所占的寬度 先放到columnWidths里面 } if (allColumsWidth + prnDgv.ColumnCount < PrintPaper.Width) { int columnAddWidth = (PrintPaper.Width - allColumsWidth - prnDgv.ColumnCount) / prnDgv.ColumnCount; for (int i = 0; i < prnDgv.ColumnCount; i++) { columnWidths[i] += columnAddWidth; } } //************************************************************* currentX = 0; int titleHeight = (int)Math.Ceiling(graphic.MeasureString("1e{(汗", titleFont).Height); //打印表頭 for (int i = 0; i < prnDgv.ColumnCount; i++) { string celValue = prnDgv.Columns[i].HeaderText; //打印背景 graphic.FillRectangle(new SolidBrush(titleBackGroup), new Rectangle((int)currentX, (int)currentY, columnWidths[i], titleHeight)); //打印內容 graphic.DrawString(celValue, titleFont, titleBrush, currentX, currentY); //打印表格邊框 graphic.DrawRectangle(new Pen(titleBrush), new Rectangle((int)currentX, (int)currentY, columnWidths[i], titleHeight)); currentX = currentX + columnWidths[i]; } currentX = 0; currentY = currentY + titleHeight; int contentHeight = (int)Math.Ceiling(graphic.MeasureString("1e{(汗", defaultFont).Height); //打印內容 for (int i = 0; i < prnDgv.Rows.Count; i++) { for (int j = 0; j < prnDgv.ColumnCount; j++) { string celValue = prnDgv[j, i].Value.ToString();//打印內容 graphic.DrawString(celValue, defaultFont, defaultBrush, currentX, currentY);//打印表格邊框 graphic.DrawRectangle(new Pen(defaultBrush), new Rectangle((int)currentX, (int)currentY, columnWidths[j], contentHeight)); currentX = currentX + columnWidths[j]; } currentX = 0; currentY = currentY + contentHeight; } } }
調用示例代碼
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) { PrintCore print = new PrintCore(printDocument1, e.Graphics, new Font("宋體", 14)); if (print.Error != null) { e.Cancel = true; MessageBox.Show(print.Error); return; } print.isCenter = true; print.defaultFont = new Font("宋體", 16); print.printString("定積分落實到減肥了減肥了聖誕節死減", true); print.isCenter = false; print.defaultFont = new Font("宋體", 14); print.printString("111定積分落實到減定積分落實到減肥了聖誕節死定了減肥了束帶結發連鎖店減肥了哦定積分落實到減肥了聖誕節死定了減肥了束帶結發連鎖店減肥了哦肥了聖誕節死定了減肥了束帶結發連鎖店減肥了哦"); print.printDataGridView(dataGridView1,new Font("宋體", 18),Brushes.Black,DefaultBackColor); }
總結:以上打印有兩個小問題沒有處理。一個是關於分頁,一個是當表格的寬度過長,超過了頁面的寬度,沒有進行換行處理。
