C#:使用FastReport打印带图片传参模板的实现方法
大家都知道,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打印带图片传参模板的实现方法的更多相关文章
- java:打印菱形图案(传参打印的自定义字符和行数)
打印菱形图案: 代码实现: public class Hello { public static void main(String args[]) { LingXingPrint("#&qu ...
- Angular页面传参的四种方法
1. 基于ui-router的页面跳转传参 (1)在Angular的app.js中用ui-route定义路由,比如有两个页面, 一个页面(producers.html)放置了多个producers,点 ...
- flask开启debug模式的两种方法、加载配置文件的两种方法、URL传参的四种方法
from flask import Flask app = Flask(__name__) # app.config.update(DEBUG=True)#开启debug模式 #加载配置文件方法一 # ...
- 微信小程序的页面跳转==编程式导航传参 和 标签的方法传参==以及如何过去传递过来的参数
小程序导航传参接收传递过来的参数 在onload中 实例
- angularJS 传参的四种方法 【修改】
1. 基于ui-router的页面跳转传参(1) 在AngularJS的app.js中用ui-router定义路由,比如现在有两个页面,一个页面(producers.html)放置了多个produce ...
- angularJS 传参的四种方法
AngularJS - Passing data between pages 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:Ye Huang链接:https://www.z ...
- vue 路由传参的三种方法
API在这里 https://router.vuejs.org/guide/essentials/navigation.html 第一种传参 通过路由属性中的name来确定匹配的路由,通过param ...
- Angular5 路由传参的3种方法
一共3种方法. 1.问号后面带的参数,获取参数的方式:ActivatedRoute.queryParams[id] 例如:/product?id=1&name=iphone还可以是: [rou ...
- vue路由跳转传参的两种方法
路由跳转: this.$router.push({ name: '工单列表', params: {p_camera_dev_name: 'xxx'} }); 使二级菜单呈点击状态: $('[index ...
随机推荐
- 原型链、构造函数、箭头函数、se6数组去重
原型链 例子如下: var arr = [1, 2, 3]; 其原型链为:arr ----> Array.prototype ----> Object.prototype ----> ...
- Java Web项目实战第1篇之环境搭建
写在前面的话 从今天开始一个Java Web实战项目,参考自 http://blog.csdn.net/eson_15/article/details/51277324 这个博客(非常感谢博主的分享精 ...
- Marshal.ReleaseComObject() vs. Marshal.FinalReleaseComObject()
很简单,不翻译了. If you are using COM components on your .NET code, you might be already aware of the Marsh ...
- Navicat premium连接Oracle报ORA-28547错误
1:ORA-28547 原因:navicate Primium版本的OCi和本地数据库的OCI版本不一致. 解决方法: 1:把navicate Primium版本自带oci.dll替换本地Oracle ...
- mac上的svn命令
Mac 终端添加代码到SVN从SVN拉取代码步骤:1.cd /Users/指定路径(本地路径)2.svn checkout https://xxx.xxx.xx/svn/project(服务器路径) ...
- bupt summer training for 16 #2 ——计算几何
https://vjudge.net/contest/171368#overview A.一个签到题,用叉积来判断一个点在一条线的哪个方向 可以二分,数据范围允许暴力 #include <cst ...
- Git学习总结(13)——使用git.oschina作为自己的源代码在线管理库
工作有几年了,期间积累了很多的代码片段,一直想找个存放的地方,方便随时的取用.以前可能是放在自己电脑的硬盘中,但毕竟这样使用起来还是有很多不便. 下面通过码云来说明 一下设置过程.其实,码云和GitH ...
- [HDU3518]Boring counting(后缀数组)
传送门 求出现超过1次的不重叠子串的个数 根据论文中的方法. 枚举子串的长度 k. 用 k 给 height 数组分组,每一组求解,看看当前组的位置最靠后的后缀和位置最靠前的后缀所差个数是否大于长度, ...
- zoj 3693
#include<stdio.h> #include<string.h>//进位问题如3.985 应该进位3.99 int main() { int n,k,i; ...
- Lifting the Stone 计算几何 多边形求重心
Problem Description There are many secret openings in the floor which are covered by a big heavy sto ...