WPF打印票据

news/2024/11/30 5:54:29/

最近工作的内容是有关于WPF的,整体开发没有什么难度,主要是在打印上因为没有任何经验,犯了一些难,不过还好,解决起来也不是很费劲。

WPF打印票据或者是打印普通纸张区别不大,只是说打印票据要把需要打的内容摆放好位置,搞定缩放比例,就可以放入票据直接打印了。
那么关键点就是3个:
1、使用WPF提供的什么类、什么方法来执行打印
2、如何摆放位置
3、如何搞定缩放比例

1、使用WPF提供的什么类、什么方法来执行打印

这个问题很容易解决,搜索下WPF打印或WPF Print,就能找到示例代码。
那么我用的是PrintDialog的PrintVisual方法。PrintDialog从名字中可以看出是个对话框,让用户手动选择打印机。如果不想弹出对话框和选择打印机,则可以读取默认打印机或者在配置文件里配置打印机名称,然后找到它。这就需要用到另外的两个类:PrintQueue和LocalPrintServer。
使用PrintDialog打印:

var printDialog = new PrintDialog();
printDialog.PrintQueue = GetPrinter();
printDialog.PrintVisual(visual, visual.Name);

获取打印机任务队列:

public static PrintQueue GetPrinter(string printerName = null)
{try{PrintQueue selectedPrinter = null;if (!string.IsNullOrEmpty(printerName)){var printers = new LocalPrintServer().GetPrintQueues();selectedPrinter = printers.FirstOrDefault(p => p.Name == printerName);}else{selectedPrinter = LocalPrintServer.GetDefaultPrintQueue();}return selectedPrinter;}catch{return null;}
}

2、如何摆放位置

注意到我们上面的打印代码是使用的PrintVisual,参数是Visual,那么这个Visual是什么?
我举个WPF Grid类的继承关系:Grid : Panel : FrameworkElement : UIElement : Visual,所以WPF的控件都是继承自UIElement的,也是继承Visual的。
那么我们把Grid看作是一张票据或一张纸,在这张纸上布置好需要打印的内容,不就OK了吗。
你可以创建一个用户控件来鼠标拖拽摆放,传入实体对象绑定值,也可以动态生成一个Grid。

3、如何搞定缩放比例

仅仅摆放好,打印出来未必是我们想要的结果。因为票据的大小不同,特别是银行那种身份证或金额的小格子,打歪了只能说明技术不到家啊。
所以摆放是要有依据的,依据就是扫描票据,然后在扫描的底图上摆放,样位置就不会错位。然后缩放就是DPI(DPI是Dots Per Inch(每英寸所打印的点数)的缩写)的概念。我们扫描的图是像素的,而实际的纸张不能用像素这个单位。这个之间的换算需要依赖DPI。
具体缩放的方法:

//注意,我这里DPI写死的是150,实际中你的DPI是多少要看扫描件怎么扫的。
var settings = new PrintSettings { Width = visual.Width, Height = visual.Height, DPI = 150 };
var renderTarget = new RenderTargetBitmap((int)settings.Width, (int)settings.Height, settings.DPI, settings.DPI, PixelFormats.Default);
printDialog.PrintTicket = new PrintTicket();
printDialog.PrintTicket.PageMediaSize = new PageMediaSize(renderTarget.Width, renderTarget.Height);
var capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);
var scale = Math.Max(capabilities.PageImageableArea.ExtentWidth / visual.Width, capabilities.PageImageableArea.ExtentHeight / visual.Height);
visual.LayoutTransform = new ScaleTransform(scale, scale);
var sz = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
visual.Measure(sz);
visual.Arrange(new Rect(new Point(0, 0), sz));

 

这样我们就达到了缩放的目的,你可以查看MSDN看看具体的类和方法的含义。

其他的需求:

1、竖打

有些单据比较窄,但是宽度还可以,所以希望可以竖着打印,满足这个需求也是一句话的事情。
在visual.Measure(sz);语句之前增加下面两行代码即可。

printDialog.PrintTicket.PageOrientation = PageOrientation.Landscape;
printDialog.PrintTicket.PageMediaSize = new PageMediaSize(renderTarget.Height, renderTarget.Width);

2、退纸(针式打印机)

退纸并不是常用的功能,但是放错了纸张想拿出来也要费一番力气,所以想让打印机自动吐出纸来。我也搜索了很多问答和文章,也没试出来一个成功的,可能是方法不正确。最终采用了一个比较鸡贼的办法,就是打印一个空白页,然后自动退纸。每种针式打印机可能不同,所以退纸的空白页的大小要调整好。

var printer = GetPrinter();
var visual = new Grid()
{
Width = 1000,
Height = 1500,
VerticalAlignment = VerticalAlignment.Top,
HorizontalAlignment = HorizontalAlignment.Left
};
PrintVisual(printer, visual);

 3、监控打印任务状态

打印的时候肯定想知道任务有没有被打印,提醒用户放入纸张,打印完毕后提醒用户打印完成。我这里写了一个PrintJobChecker类,Start后就会根据timer的间隔时间检查任务队列,和打印时间。
但是.NET提供的方法并不能很好的做到理想的效果,只能获取到任务还有没有,这是很郁闷的事情。一旦打印机开始打印(注意还没完成),job就是null了。这无法判断纸张是不是还在打印中。如果有朋友知道怎么处理还望评论告知。

public class PrintJobChecker
{private DispatcherTimer _timer;private PrintQueue _printer;private Action<string> _checkingAction;public DateTime? StartPrintTime { get; set; }private int _interval = 100;public int TimerInterval{get { return _interval; }set{_interval = value;_timer.Interval = TimeSpan.FromMilliseconds(value);}}public PrintJobChecker(PrintQueue printer, Action<string> checkingAction){if (printer == null || checkingAction == null){return;}_printer = printer;_checkingAction = checkingAction;_timer = new DispatcherTimer{Interval = TimeSpan.FromMilliseconds(TimerInterval),};_timer.Tick += CheckJobStatus;PrintingStatus = "正在打印";PrintErrorStatus = "打印出错";PrintOfflineStatus = "请连接打印机";PrintWaittingStatus = "请放入相应的表单至打印机";PrintUnknownStatus = "未知错误";}public void Start(){_timer.Start();}public void Stop(){_timer.Stop();}private void CheckJobStatus(object sender, EventArgs e){if (_printer == null){return;}var job = _printer.GetLastJob();if (job == null){if (!StartPrintTime.HasValue){StartPrintTime = DateTime.Now;}_checkingAction(PrintingStatus);}else{var statusText = GetJobStatus(job);_checkingAction(statusText);}}public string PrintingStatus { get; set; }public string PrintErrorStatus { get; set; }public string PrintOfflineStatus { get; set; }public string PrintWaittingStatus { get; set; }public string PrintUnknownStatus { get; set; }private string GetJobStatus(PrintSystemJobInfo job){if (job == null) return null;if (((job.JobStatus & PrintJobStatus.Completed) == PrintJobStatus.Completed)||((job.JobStatus & PrintJobStatus.Printed) == PrintJobStatus.Printed)){StartPrintTime = DateTime.Now;return PrintingStatus;}if ((job.JobStatus & PrintJobStatus.Error) == PrintJobStatus.Error){_timer.Stop();return PrintErrorStatus;}if ((job.JobStatus & PrintJobStatus.Offline) == PrintJobStatus.Offline||job.JobStatus == PrintJobStatus.None){return PrintOfflineStatus;}if ((job.JobStatus & PrintJobStatus.Printing) == PrintJobStatus.Printing){if (job.TimeSinceStartedPrinting > 0){return PrintingStatus;}else{return PrintWaittingStatus;}}return PrintUnknownStatus;}
}

  

转载于:https://www.cnblogs.com/mad/p/4594962.html


http://www.ppmy.cn/news/382099.html

相关文章

关于C++数组名和指针的一些思考

在学习指针数组与数组指针一节时&#xff0c;了解到数组名其实是指向数组收个元素的指针。如下面代码所示 int main() {int a[5] {1, 2, 3, 4, 5};cout << "*a:" << *a << endl;cout << "*(a 1):" << *(a 1) << e…

水文水动力模型在城市内涝、城市排水、海绵城市规划设计中教程

详情点击链接&#xff1a;水文水动力模型在城市内涝、城市排水、海绵城市规划设计中应用教程 一&#xff0c;CAD、GIS水力建模过程 1.1复杂城市排水管网系统快速建模&#xff1a;通过标准化的步骤&#xff0c;利用CAD数据、GIS数据建立SWMM模型。在建模的不同阶段发挥不同软…

Python设计模式——行为型模式

目录 责任链模式观察者模式策略模式模板方法模式 责任链模式 内容&#xff1a;使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链&#xff0c;并沿着这条链传递该请求&#xff0c;直到有一个对象处理它为止。角色 抽象…

技嘉GA7PESH3主板,官网驱动下载,官方使用说明

主板如图 驱动下载 服务器主板驱动比较难找&#xff0c;这里给出官网地址 http://b2b.gigabyte.com/products/product-page.aspx?pid4468#dl 用户手册 也在那个地址&#xff0c;选择一下就好了 https://download.gigabyte.com/FileList/Manual/server_manual_7pesh3_sc_100…

第14届蓝桥杯Scratch(中级)省赛真题解析2023.5.14

选择题 1. 已知下图角色一共有3个造型,则以下选项中,不能呈现下图中第三个造型效果的程序是(C) *选择题严禁使用程序验证,选择题不答或答错都不扣分 A. B. C. D. 2. 运行以下程序,循环执行4次后,x的值是(D)。 *选择题严禁使用程序验证,选择题不答或答错都不扣分

java基础(多线程)-变量的线程安全分析

一、变量的线程安全分析 1.1 成员变量和静态变量是否线程安全 如果他们没有共享&#xff0c;则线程安全如果他们被共享了&#xff0c;根据他们的状态是否能够改变&#xff0c;有份两种情况 如果只有读操作&#xff0c;则线程安全如果有读写操作&#xff0c;则这段代码是临界…

【链表复习】C++ 链表复习及题目解析 (2)

目录 牛客 CM11 链表分割 牛客 OR36 之链表的回文结构 Leetcode 160. 相交链表 LeetCode 141. 环形链表 LeetCode 138. 复制带随机指针的链表 本文继续延续前文&#xff0c;为大家带来几道经典的链表中等难度的题目。 牛客 CM11 链表分割 现有一链表的头指针 ListNode* p…

方太jcd7和jcd9a哪个好真假鉴别,优缺点评测感受

方太jcd7和jcd9a哪个好真假鉴别,优缺点评测感受易清洁&#xff0c;挥手即用&#xff0c;极其方便。安装师傅态度好、技术好&#xff0c;非常感谢&#xff01; 物流很快&#xff0c;第一天下单&#xff0c;第二天就到&#xff0c;师傅安装很好&#xff0c;玻璃面板&#xff0c;高…