大家都知道,C#打印图片可以直接调用PrintDocument控件的PrintPage事件,通过画刷对image对象直接进行绘制。但是这种方法存在局限,例如如果打印的图片需要按纸张大小进行缩放的话,那么图片显示比例和图片显示位置等都需要动态计算,如果还要添加水印或者其他的图片操作,基本上要添加很多额外的逻辑,并且效率不高,严重限制了程序的性能。如果要在图片上绘制个性化的文本或者定制其他内容,则基本没办法实现,严重限制了程序的可扩展性和可维护性。

常规写法如下所示:

 //例如这是PrintDocument的PrintPage事件
Graphics g = null;
g = e.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality; //设置画刷高质量 //绘制已经处理过的Bitmap对象(假设它已经从服务器或者某个地方下载下来并且已经算好了在纸张上指定的打印位置)
//此种写法在激光高速打印机中存在明显缺陷,因此不建议大业务量的程序使用
g.DrawImage(bmp, locationX, locationY, bmp.Width, bmp.Height);

笔者使用某款佳能的普通喷墨打印机速度不是很理想,在实际业务需求量很大的情况下,采用了利盟牌某款高速激光打印机,但是调用PrintDocument控件对图像对象进行绘制时,打印机处于等待状态,虽然打印任务已经发送过去,但是由于图片尚未绘制完成,所以打印机停滞不出纸张,效率还不如普通喷墨打印机,可以说这是这种方法的局限性导致的,因此在大业务量的情况下,使用这种方法进行打印明显并不合适。

所以我就开始研究使用模板进行打印的方法。

本文所探讨的是使用FastReport第三方控件对图片打印进行个性化的模板定制。首先在项目中引入此控件的相关dll。

第一步:添加FastReport模板打印相关类和方法:

在打印类中,我们定义一个打印模板的方法,关键代码如下:

         /// <summary>
/// 打印报表
/// </summary>
/// <param name="ht">fastreport参数(key)及值(value)</param>
/// <param name="ds">数据集</param>
/// <param name="functionCode">模板名称</param>
/// <param name="modelCode">模板类型</param>
/// <param name="selectPrint">是否选择打印机bool</param>
public void Print(NoSortHashTable ht, DataSet ds, string functionCode, string modelCode, bool selectPrint)
{
//假设模板数据存在数据库中,此时先获取模板数据
Model.FR_Template m = this.GetFastReportModel(functionCode, modelCode, orgid); if (m == null)
{
throw new Exception("调用的模版不能为空!");
}
//设置模版数据
this.TempContent = string.IsNullOrEmpty(this.TempContent) ? m.TEMPCONTENT : this.TempContent; //TempInf为空时报错
if (!string.IsNullOrEmpty(this.TempContent))
{
//导入模版数据
this.report.LoadFromString(this.TempContent);
} if (FRds != null)
{
// 注册报表数据
this.report.RegisterData(ds, FRds.DataSetName); //加载可用的数据源
foreach (DataTable dt in FRds.Tables)
{
this.report.GetDataSource(dt.TableName).Enabled = true;
}
}
//动态添加fastreport参数
foreach (DictionaryEntry de in ht)
{
string ParamName = de.Key.ToString();
//获取参数
FastReport.Data.Parameter param = this.report.Parameters.FindByName(ParamName);
if (param != null)
{
param.Value = de.Value;
}
} this.report.PrintSettings.ShowDialog = selectPrint; string printerName = ConfigurationManager.AppSettings[functionCode] == null ? "" : ConfigurationManager.AppSettings[functionCode].ToString();
if (!string.IsNullOrEmpty(printerName))
{
this.report.PrintSettings.Printer = printerName;
}
// 运行报表打印
this.report.Print();
// 释放使用的资源
this.report.Dispose();
}

在设置打印模板的界面中,打开FastReport设计器的代码如下:

 SaveFRTemplateFrm saveFRTfrm = new SaveFRTemplateFrm(VoidNameEnum.Update, dgvr);
saveFRTfrm.Owner = this;
saveFRTfrm.StartPosition = FormStartPosition.CenterScreen;
if (saveFRTfrm.ShowDialog() == DialogResult.OK)
{
//保存设计好的打印模板
}

第二步:传递参数和数据,调用打印 

模板设计好之后,在打印的界面中需调用刚刚封装的打印方法对模板进行传参打印。一下为打印方法:

      /// <summary>
/// 从打印模板打印数据
/// </summary>
private void PrintPaperByTemplet( DataTable dsRSPrint)
{
NoSortHashTable nht = new NoSortHashTable();
SavePrintTempFile(); //添加打印参数
nht.Add("打印页码", (pagenum == ? "" : Currentpagenum + "/" + pagenum));
nht.Add("打印时间", AppData.SysDate.ToString("yyyy-MM-dd hh:mm:ss")); DataSet ds = new DataSet("DataPrint");
ds.Tables.Add(dsRSPrint.Copy()); try
{
Print(nht, ds, functionCode, "A4", false);
Application.DoEvents();
}
catch (Exception ex)
{
//errorMsg += "图片数据出现问题,无法输出到打印模板!\n";
}
}

那么关键的地方是,打印的图片数据如传入到FastReport模板中呢?有如下两种方法供你参考:

方法一:

在FastReport模板中添加图片对象的控件,指定本地或网络路径(注意必须是固定链接)的图片名称,每次打印之前先把需要打印的图片存放到这个路径并命名成指定的文件名。

        /// <summary>
/// 保存打印模板用到的临时缓存文件
/// </summary>
private void SavePrintTempFile()
{
bool isSaveFlag = true;
do
{
try
{
if (File.Exists("某个文件.jpg"))
{
File.Delete("某个文件.jpg");
Thread.Sleep(); //休眠 避免保存文件时图片尚未删除
}
img.Save(printTempFile); using (Bitmap bmpPrint = new Bitmap(img))
{
//对图片进行一些处理,例如压缩大小,调整对比度等等
}
}
catch (Exception ex)
{
isSaveFlag = false;
}
} while (!isSaveFlag); //将文件设置为隐藏
FileInfo fi = new FileInfo(printTempFile);
File.SetAttributes(printTempFile, fi.Attributes | FileAttributes.Hidden);
}

当然如果你想在图片上添加水印,在模板中也可以实现,例如下图片所示,在图片层上面指定水印图片,注意必须是PNG格式的矢量图形,否则会盖住原始的图片内容。

水印的添加设置方法同上面的图片添加,在模板中设置指定路径即可。

方法二:

将图片对象通过数据列或参数形式传递到模板中,注意需要将image对象格式转化为64位字符串。

 Byte[] streamByte = ImageBytesHelper.GetByteImage(img);  //先将image对象转化为二进制字节(过程略)
dataRow["图片数据"] = Convert.ToBase64String(streamByte); //再将字节转换为64为字符

在模板中,你需要添加部分事件代码解析传过来的图片数据。

//在模板的DataPrintBefore事件中写下如下代码
string imgStr = (string)Report.GetColumnValue("ds.图片数据"); byte[] imgData=Convert.FromBase64String(imgStr);
MemoryStream ms = new MemoryStream(imgData);
Image img = System.Drawing.Image.FromStream(ms); //PictureObject pic=Report.FindObject('Picture1') as PictureObject;
Picture1.Image=img;

此时image对象的64位字符即可以解析为图片显示在模板上了。

这两种方法是我研究了一段时间的结果,第一种直接存文件每次读取简单有效,并不影响打印效率。第二种方法传参设置,比第一种方法稍微复杂,但不需要读盘,稳定型更好。

注意:本文为Healer007原创,署名为小萝卜,本人站点:itoku.cn,欢迎交流学习,转载文章请注明出处。

C#:使用FastReport打印带图片传参模板的实现方法的更多相关文章

  1. java:打印菱形图案(传参打印的自定义字符和行数)

    打印菱形图案: 代码实现: public class Hello { public static void main(String args[]) { LingXingPrint("#&qu ...

  2. Angular页面传参的四种方法

    1. 基于ui-router的页面跳转传参 (1)在Angular的app.js中用ui-route定义路由,比如有两个页面, 一个页面(producers.html)放置了多个producers,点 ...

  3. flask开启debug模式的两种方法、加载配置文件的两种方法、URL传参的四种方法

    from flask import Flask app = Flask(__name__) # app.config.update(DEBUG=True)#开启debug模式 #加载配置文件方法一 # ...

  4. 微信小程序的页面跳转==编程式导航传参 和 标签的方法传参==以及如何过去传递过来的参数

    小程序导航传参接收传递过来的参数 在onload中 实例

  5. angularJS 传参的四种方法 【修改】

    1. 基于ui-router的页面跳转传参(1) 在AngularJS的app.js中用ui-router定义路由,比如现在有两个页面,一个页面(producers.html)放置了多个produce ...

  6. angularJS 传参的四种方法

    AngularJS - Passing data between pages 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:Ye Huang链接:https://www.z ...

  7. vue 路由传参的三种方法

    API在这里  https://router.vuejs.org/guide/essentials/navigation.html 第一种传参 通过路由属性中的name来确定匹配的路由,通过param ...

  8. Angular5 路由传参的3种方法

    一共3种方法. 1.问号后面带的参数,获取参数的方式:ActivatedRoute.queryParams[id] 例如:/product?id=1&name=iphone还可以是: [rou ...

  9. vue路由跳转传参的两种方法

    路由跳转: this.$router.push({ name: '工单列表', params: {p_camera_dev_name: 'xxx'} }); 使二级菜单呈点击状态: $('[index ...

随机推荐

  1. JeePlus 工作流版本 sping mvc oa crm erp java html5 源码

    https://shop108220642.taobao.com/search.htm?spm=2013.1.w5002-5297459241.1.mnhAZ5&search=y http:/ ...

  2. 【解题报告】 洛谷 P3492 [POI2009]TAB-Arrays

    [解题报告] 洛谷 P3492 [POI2009]TAB-Arrays 这题是我随机跳题的时候跳到的.写完这道题之后,顺便看了一下题解,发现只有一篇题解,所以就在这里顺便写一个解题报告了. 首先当然是 ...

  3. POJ3278——Catch That Cow

    Catch That Cow Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 114140   Accepted: 35715 ...

  4. Books Queries (codeforces 1066C)

    模拟题 开一个容器进行模拟即可,注意容器设置初始大小不然容易re.设置两个指针l,r.把容器当作桶,每一个桶都有一个编号表示位置,左边进入那么就是编号为l,右边一样.然后l--或者r++,l=r=0的 ...

  5. Linux自动化之基于http的pxe安装服务

    PXE:     Preboot Excution Environment 预启动执行环境     Intel公司研发     基于Client/Server的网络模式,支持远程主机通过网络从远端服务 ...

  6. Serial Fluent UDF on Windows

    test test Table of Contents 1. Serial UDF on Windows OS 1 Serial UDF on Windows OS Note: Udf has to ...

  7. https://github.com/MediaTek-Labs/linkit-smart-7688-feed编译失败

    mkdir -p /home/fly/workdir/LinkltSmart7688Duo-20170626/openwrt/dl/home/fly/workdir/LinkltSmart7688Du ...

  8. 【codeforces 760D】Travel Card

    [题目链接]:http://codeforces.com/contest/760/problem/D [题意] 去旅行,有3种类型的乘车票; 第一种:只能旅行一次20元 第二种:按时间计算,90分钟内 ...

  9. SSM(spring mvc+spring+mybatis)学习路径——2-1、spring MVC入门

    目录 2-1 Spring MVC起步 一.回顾Servlet 二.SpringMVC简介 三.搭建SpringMVC第一个案例 四.简单流程及配置 五.使用注解开发Controller 六.参数绑定 ...

  10. Spring MVC-视图解析器(View Resolverr)-XML视图解析器(Xml View Resolver)示例(转载实践)

    以下内容翻译自:https://www.tutorialspoint.com/springmvc/springmvc_xmlviewresolver.htm 说明:示例基于Spring MVC 4.1 ...