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 ...
随机推荐
- LINUX-查看进程内环境变量
ps -ef find PID cat /proc/$PID/environ | grep ENV
- python tkinter模块 创建窗口V1.2
先上图 代码如下 #-*-coding:utf-8-*- import os from tkinter import * root=Tk() root.title('执行窗口') "&quo ...
- c/c++排坑(2) -- c语言中的符号重载
所谓的符号重载就是在不同的上下文环境里有不同的意义.甚至有些关键字也被重载而具有好几种意义,这也是C语言的作用域规则对程序员不那么清晰的主要原因. 本章内容摘自<c专家编程>P37. 大家 ...
- pip/pip3国内源
Error 在使用pip3安装PySide2时出现ReadTimeoutError. $ pip3 install PySide2 Solution 使用国内源 例如: $ pip3 install ...
- vue 项目的I18n国际化之路
I18n (internationalization ) ---未完善 产品国际化是产品后期维护及推广中重要的一环,通过国际化操作使得产品能更好适应不同语言和地区的需求 国际化重点:1. 语言语言本地 ...
- Django REST framework - 认证
目录 认证 DRF 5种验证方式 如何确定身份验证? 设置身份验证方案 案例: 基于自定义Token认证 第一步: 定义一个用户表和一个保存用户Token的表 第二步: 定义一个登陆视图 第三步定义一 ...
- 《零压力学Python》 之 第二章知识点归纳
第二章(数字)知识点归纳 要生成非常大的数字,最简单的办法是使用幂运算符,它由两个星号( ** )组成. 如: 在Python中,整数是绝对精确的,这意味着不管它多大,加上1后都将得到一个新的值.你将 ...
- [Usaco2007 Dec]队列变换
[Usaco2007 Dec]队列变换 题目 FJ打算带他的N(1 <= N <= 30,000)头奶牛去参加一年一度的“全美农场主大奖赛”.在这场比赛中,每个参赛者都必须让他的奶牛排成一 ...
- 基于CNONIX国家标准的出版社ERP系统建设分享
目录 一.出版社ERP系统建设面临的三大挑战 在系统建设中如何贯彻CNONIX国家标准 新ERP系统建设面临的挑战 技术体系及架构选择面临的挑战 二.系统建设实施过程控制 项目组织管控 项目技术管控 ...
- Square words(codevs 3301)
题目描述 Description 定义square words为: 1.长度为偶数. 2.前一半等于后一半. 比如abcabc和aaaa都是square words,但是abcabcab和aaaaa都 ...