时常在网络上搜索关于打印格式的设计方面的资料,发现资料比较少,前一段时间因为有一个项目有这样一个需求,需要客户自定义打印格式,自己干脆做了一个,基本能满足要求.
本文章已经发布了三个版本,越靠后面 版本越新,功能更完善.
https://download.csdn.net/download/guo9long/89450604
https://download.csdn.net/download/guo9long/89623501
https://download.csdn.net/download/guo9long/89642363
(包含Demo,测试的模板,可以直接运行debug里面的exe看效果.)
20240912已经发布了 可拖拉的最新版,链接地址如下:
C# 可视化 拖拉控件 打印格式设计 模板设计 单据打印 标签打印 使用教程-CSDN博客
感兴趣的朋友可以去关注下。
开发环境:vs2019 + Dev express 20.1(如果不用修改模板设计代码, 可不用安装DevExpress环境.)
引用下载的DLL后,把里面的 frmFormatForm 设计窗体show出来.
HY.PrintSet.frmFormatSetForm f1 = new HY.PrintSet.frmFormatSetForm();
f1.Show();
第一步 先新建一个容器,该容器决定打印的纸张大小,然后点击 工具栏的控件,按需添加控件到容器中.默认放在容器的左上角,叠在一起可能看不到,需要挪开上层控件,下面的才能看到.
目前只能通过右边字段属性进行调整,不能在预览界面拖拉控件.(下一版本 推出在预览界面直接拖拉控件,当前是0815版本)
可拖拉控件的版本已经发布:如下链接
C# 可视化 拖拉控件 打印格式设计 模板设计 单据打印 标签打印 使用教程-CSDN博客
还可以添加表格,再添加列,就能看到效果
每一列相当于是 一个活动文本, 打印时可以用数据里面的绑定的数据 替换.
耐心添加完,就可以你这样子
打印模板设计好后,保存到本地一个文件,(后缀.fmt),大家有需要可以自己修改代码,比如保存模板到数据库,打印时按字段读取或者json文本保存读取. 这里我们用模板文件做演示.
当前保存到本地文件json格式保存, 打印时先读取模板的json内容, 读取代码可以参考如下:(demo里面也有)
private string ReadFormat(string filename)
{
string json = "";
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs, Encoding.UTF8);
string strLine = "";
while ((strLine = sr.ReadLine()) != null)
{
json += strLine;
}
return json;
}
通过Newtonsoft.Json 反序列化一下,得到模板对象.
JsonSerializerSettings jsset = new JsonSerializerSettings();
jsset.TypeNameHandling = TypeNameHandling.All;
PFormat fmat = JsonConvert.DeserializeObject<PFormat>(json, jsset);
最关键的是构建符合要求的打印数据:
目前支持2种方式:
#region 构建打印数据 方式一,Dic单头 + DataTable 单体
Dictionary<string, string> po = new Dictionary<string, string>();
po.Add("fbillno", "PO20240808001");
po.Add("fsupplier", "中山市供应商有限公司");
po.Add("fnote", "测试单头备注");
po.Add("fdate", DateTime.Now.ToString("yyyy-MM-dd"));
DataTable ens = new DataTable();
ens.Columns.Add("fnumber"); //物料Code
ens.Columns.Add("fname"); //物料名称
ens.Columns.Add("funit");//单位
ens.Columns.Add("fqty"); //数量
ens.Columns.Add("fprice");//单价
ens.Columns.Add("famount"); //金额
ens.Columns.Add("fnote");//行备注
DataRow dr = ens.NewRow();
decimal qtys = 0;
decimal Amount = 0;
for (int r = 1; r < 10; r++)
{
dr = ens.Rows.Add();
dr[0] = "M00" + r;
dr[1] = "吸顶灯" + r;
dr[2] = "PCS";
dr[3] = "200";
dr[4] = "36.00";
dr[5] = "7200.00";
dr[6] = "测试行备注" + r;
qtys += 200;
Amount += 7200;
}
//数据里面自己添加合计行,后续可以看下模板是否支持
dr = ens.Rows.Add();
dr[0] = "总合计:";
dr[3] = qtys;
dr[5] = Amount;
#endregion 构建打印数据
#region 构建打印数据 方式二 Model方式
PO po = new PO()
{
fbillno = "PO20240808001",
fsupplier = "中山市供应商有限公司",
fdate = DateTime.Now.ToString("yyyy-MM-dd"),
fnote = "测试单头备注"
};
List<POEntry> ens = new List<POEntry>();
for (int r = 1; r <= 10; r++)
{
POEntry en = new POEntry()
{
fnumber = "M00" + r,
fname = "吸顶灯" + r,
funit = "PCS",
fqty = "100.00",
fprice = "35.00",
famount = "3500.00",
fnote = "测试行备注" + r
};
ens.Add(en);
}
//数据里面自己添加合计行,后续版本可以完善模板参数控制 合计行,包含页小计,最后页总合计.
POEntry ensum = new POEntry()
{
fnumber = "总合计:",
fname = "",
funit = "",
fqty = "1000.00",
fprice = "",
famount = "35000.00",
fnote = ""
};
ens.Add(ensum);
#endregion 构建打印数据
public class PO
{
public string fbillno { get; set; } //单号
public string fsupplier { get; set; } //供应商
public string fdate { get; set; } //日期
public string fnote { get; set; } //备注
}
public class POEntry
{
public string fnumber { get; set; } //物料Code
public string fname { get; set; } //物料名称
public string funit { get; set; } //单位
public string fqty { get; set; } //数量
public string fprice { get; set; } //单价
public string famount { get; set; } //金额
public string fnote { get; set; } //行备注
}
public class Product
{
public string bcode { get; set; }
public string fname { get; set; }
public string model { get; set; }
public string specs { get; set; }
}
模板有了,数据有了,接下来就是调用打印服务,开始打印.
// Dic +DataTable 方式 调用方法
PrintService ps = new PrintService(false, fmat, po, ens, false, 0, 0);
//打印预览
ps.PrintView();
//打印需要打印机名称
//ps.Print("打印机名称");
//Model 调用方法
PrintServiceT<PO, POEntry> ps = new PrintServiceT<PO, POEntry>(false, fmat, po, ens, false, 0, 0);
//打印预览
ps.PrintView();
上面是单据类打印,如果是标签类打印,也是可以的,构建的数据 调用一次打印一张, 多张就循环调用,
后续这里也可以更改成传入List<>, 一次打完全部.
图片控件 需要将图片事先拷贝到软件的当前目录,当前设置是这样.其他目录打印时读取不到.
#region 构建打印数据 标签类Did+DT方式
Dictionary<string, string> pd = new Dictionary<string, string>();
pd.Add("bcode", DateTime.Now.ToString("yyyyMMddHHmmss"));
pd.Add("fname", "吸顶灯1");
pd.Add("model", "86141-551");
pd.Add("specs", "35*35*20");
#endregion 构建打印数据
#region 构建打印数据 Model方式
Product pd = new Product()
{
bcode= DateTime.Now.ToString("yyyyMMddHHmmss"),
fname= "吸顶灯1",
model= "86141-551",
specs= "35*35*20"
};
#endregion 构建打印数据
#region 调用打印
PrintService ps = new PrintService(false, fmat, pd, null, false, 0, 0);
//打印预览
ps.PrintView();
#endregion
//Model方式
//第二个T ,用不到,随便一个Model都行
PrintServiceT<Product, Product> ps = new PrintServiceT<Product, Product>(false, fmat, pd, null, false, 0, 0);
//打印预览
ps.PrintView();
小插曲 : c# 获取本地打印机 :
//获取本地打印机列表
public static List<String> GetLocalPrinters()
{
List<String> fPrinters = new List<String>();
foreach (String fPrinterName in PrinterSettings.InstalledPrinters)
{
if (!fPrinters.Contains(fPrinterName))
{
fPrinters.Add(fPrinterName);
}
}
return fPrinters;
}
打印机列表可以绑定到程序界面的下拉控件让用户自己选择 . 或者设置默认.
程序在实际打印时,可能对纸张的方向以及坐标系,边距 等存在布局差异.更改坐标的原点进行调整.
PrintServiceT<Product, Product> ps = new PrintServiceT<Product, Product>(false, fmat, pd, null, false, 0, 0); 最后两个参数就是用来调整坐标原点的.
这两个参数(可以负数) 进行调整,调整到合适的位置即可.
之前看到有些朋友用BarTender 设计格式,打印时调用组件和格式文件 实现打印, 或者用水晶报表来实现, 测试发现打印速度真的是慢如牛,特别是某些办公电脑卡的很,还不怎么灵活.
还是用纯.net自带的组件(设计模板界面用到dev,客户端实际打印是纯.net),那速度杠杠的.也没有什么授权认证,这省了多少资源,经过测试,老古董电脑照样打印快的飞起.
下载地址: (越靠后的链接版本越新,功能更完善稳定)
https://download.csdn.net/download/guo9long/89450604
https://download.csdn.net/download/guo9long/89623501
https://download.csdn.net/download/guo9long/89642363 (最新版)
debug目录里面包含所需的所有Dev的DLL 以及 Demo源码. 最新版也修复了之前版本存在的一些bug.
如有疑问, 可以发邮件给我咨询(82963620@qq.com) 或者回复本帖,我会不定时查看回复的.
真的很希望能有机会跟大家一起交流学习,希望开发从业者,不要做信息孤岛,尽量开放包容. 有交流进步才快.