以前写打印都是根据打印机的型号,找开发类库。然后在此基础上开发。写的多了自然就想到了封装。这是还是想到了微软,微软封装了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); }
总结:以上打印有两个小问题没有处理。一个是关于分页,一个是当表格的宽度过长,超过了页面的宽度,没有进行换行处理。
作者:Bonker 出处:http://www.cnblogs.com/Bonker QQ:519841366 |
|