最近一段时间一直在做自定义模板打印,考虑过几个方案
一、首先考虑在winform界面中添加一个容器,然后由用户向其中拖拽控件,插入横线等。最后识别容器中控件的位置然后通过GDI+绘制到printdocument上。光想想就感觉要写很多代码,而且拖拽控件时的动作蛮丑的,会有卡顿感,所以放弃了。
二、使用报表,找了一圈也没什么特别好用的报表,主要还要实现用户自定义,那么就需要用户直接去修改报表,先不说普通用户编辑报表会不会出错,就平常电脑里谁会装报表软件啊,打都打不开。做固定几种格式倒是可以考虑使用报表,自定义就算了。
最后使用了Excel,毕竟平常电脑里office软件必不可少。主要思路就是
1、编辑模板,并在其中需要插入数据的单元格中加入特殊字符,以便在程序中识别并替换成相应的数据
2、通过代码加载模板,识别并填充数据,并利用Spire.xls存储为图片
3、打印图片
如果大家有更好的思路麻烦告知一下,这个方案个人也并不是特别满意

第一步、编辑Excel模板
excel模板
表格中带有&的即为需要填充的数据,后面的文字是数据库中的字段,方便查找数据

第二步、读取模板、填充数据并存为图片
创建一个winform界面,向其中添加一个printdocument及一个printpreviewcontrol控件

printDocument1.DefaultPageSettings.PaperSize = new PaperSize("print", (int)(width / 0.254), (int)(height / 0.254));
//设置printdocument的打印区域大小(该函数的单位是百分之一英寸,所以mm要转为百分之一英寸就需要除以0.254)
printPreviewControl1.Document = printDocument1;//设置预览控件的文档为printdocument1
printPreviewControl1.InvalidatePreview();//刷新预览控件(不需要预览也直接调用printdocument1.print()函数直接打印)
//这一段写在你需要刷新或打印的地方

在文件打印或者预览时,printdocument1将自己调用PrintPage事件

private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            DataSet ds = new DataSet();
            ds = da.DataSelect(cmdstr);//此处是查询数据库并填充到dataset中,根据自己需求自行修改
            printGDI pt = new printGDI();
            pt.custom_excel(printDocument1, e, ds);
        }
 public void custom_excel(PrintDocument printdoc, System.Drawing.Printing.PrintPageEventArgs e, DataSet ds)
        {
            string importExcelPath = AppDomain.CurrentDomain.BaseDirectory + "\\Template.xls";//模板路径
            string exportExcelPath = AppDomain.CurrentDomain.BaseDirectory + "\\print.xls";//填充后的表格保存路径
            Workbook workbook = new Workbook();//新建工作簿
            workbook.LoadFromFile(importExcelPath);//加载模板
            Spire.Xls.Worksheet sheet = workbook.Worksheets[0];//选择第一张表
            int rows = sheet.Rows.GetLength(0);//获得行数
            int columns = sheet.Columns.GetLength(0);//获得列数
            for (int i = 1; i <= rows; i++)
            {
                for (int j = 1; j <= columns; j++)
                {
                    string cellvalue = sheet.Range[i, j].Text;//获取单元格值
                    if (cellvalue != null && cellvalue.Contains("&"))//判断单元格有值且带有&字符则将其换成数据
                    {
                        string columnsname = sheet.Range[i, j].Text.Substring(1, sheet.Range[i, j].Text.Length - 1);
                        string value;
                        if (columnsname == "日期时间")
                        { value = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); }
                        else if (ds.Tables[0].Columns.Contains(columnsname))
                        { value = ds.Tables[0].Rows[0][columnsname].ToString(); }
                        else
                        { MessageBox.Show("数据库中不存在‘" + columnsname + "’字段"); return; }
                        sheet.Range[i, j].Text = value;
                    }
                }
            }
            workbook.SaveToFile(exportExcelPath);//保存需要打印的表
            sheet.SaveToImage(AppDomain.CurrentDomain.BaseDirectory + "\\print.bmp");//将表格存为图片
            Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);//获取当前屏幕DPI
            float dpiX = graphics.DpiX;
            float dpiY = graphics.DpiY;
            System.Drawing.Image bmp = System.Drawing.Image.FromFile(AppDomain.CurrentDomain.BaseDirectory + "\\print.bmp");//加载图片
            System.Drawing.Image newbmp = bmp.GetThumbnailImage((int)(printdoc.DefaultPageSettings.PaperSize.Width / 100 * dpiX),
                (int)(printdoc.DefaultPageSettings.PaperSize.Height / 100 * dpiY), () => { return false; }, IntPtr.Zero);//按照printdoc的尺寸对图片进行缩放
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;//消除图片锯齿
            e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;//指定最邻近插值法
            e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;//指定高质量低速度呈现
            e.Graphics.DrawImage(newbmp, printdoc.DefaultPageSettings.PaperSize.Width / 2 - newbmp.Width / 2, printdoc.DefaultPageSettings.PaperSize.Height / 2 - newbmp.Height / 2);//将图像绘制在printdoc正中间
        }

解释一下,为什么这代码中还有获取屏幕DPI的
主要原因就是printdocument的尺寸单位是百分之一英寸,但图片的单位是像素。为了把图片全部填充到printdocument中,就需要对图片进行缩放。DPI就是每英寸几个像素(最后打印时可能需要获取打印机的DPI,因为打印机的DPI和屏幕的DPI有可能是不一致的,目前我还没打印过,后期再调试)。新图片的尺寸=printdocument尺寸/100*dpi。

至此打印预览控件已将excel模板中需要替换的数据替换掉,并形成图片进行预览。
这种方法的缺点就在于图片进行缩放后,图片就变模糊了。好在如果按照实际大小打,模糊的效果看得并不是很明显,但总归是不如直接打印文字清晰。
如果大家有更好的思路欢迎探讨。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐