大家都知道,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. MVCHelper 请求检验

    public class MVCHelper { //有 两 个ModelStateDictionary类,别弄混乱了要使用 System.Web.Mvc 下的 //如果添加引用中找不到System. ...

  2. bat 读取当前目录指定文件信息并拼接

    bat 读取指定文件的信息并拼接成指定格式

  3. sql 区分大小写查询

    sql 区分大小写查询 select * FROM [Users] where userName collate Chinese_PRC_CS_AS='ADMIN'

  4. BUPT2017 wintertraining(16) #9

    龟速补题.目前基本弃坑.已暂时放弃 D.I 两题. 下面不再写题意了直接说解法注意事项之类,直接放contest链接. https://vjudge.net/contest/151537 A.The ...

  5. Android音乐、视频类APP常用控件:DraggablePanel(2)

     Android音乐.视频类APP常用控件:DraggablePanel(2) 附录文章1主要演示了如何使用DraggablePanel 的DraggableView.DraggablePanel ...

  6. Ubuntu 安装有道词典

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/51302546 官网首页:有道词典 其中 ...

  7. 允许MS SqlServer远程连接

    实际问题: 服务器192.168.0.103上的SQL Express数据库实例,局域网内其余机器的Sql Server Management Studio都无法连接. 在本机上,可以用“.\SqlE ...

  8. 清北学堂模拟赛d6t2 刀塔

    分析:看到最小值最大就很显然是二分了吧,二分一下最小值,把小于它的数给删掉,然后看每个数向左边能延伸多长,往右边能延伸多长,最后统计一下有没有可行答案就可以了. #include <cstdio ...

  9. propagation属性的7个传播行为

    关于propagation属性的7个传播行为 REQUIRED:指定当前方法必需在事务环境中运行,如果当前有事务环境就加入当前正在执行的事务环境,如果当前没有事务,就新建一个事务.这是默认值.即有事务 ...

  10. faster-rcnn代码阅读2

    二.训练 接下来回到train.py第160行,通过调用sw.train_model方法进行训练: def train_model(self, max_iters): ""&quo ...