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

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

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

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

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

获取打印机任务队列:

  1. public static PrintQueue GetPrinter(string printerName = null)
  2. {
  3. try
  4. {
  5. PrintQueue selectedPrinter = null;
  6. if (!string.IsNullOrEmpty(printerName))
  7. {
  8. var printers = new LocalPrintServer().GetPrintQueues();
  9. selectedPrinter = printers.FirstOrDefault(p => p.Name == printerName);
  10. }
  11. else
  12. {
  13. selectedPrinter = LocalPrintServer.GetDefaultPrintQueue();
  14. }
  15. return selectedPrinter;
  16. }
  17. catch
  18. {
  19. return null;
  20. }
  21. }

2、如何摆放位置

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

3、如何搞定缩放比例

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

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

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

其他的需求:

1、竖打

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

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

2、退纸(针式打印机)

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

  1. var printer = GetPrinter();
  2. var visual = new Grid()
  3. {
  4. Width = 1000,
  5. Height = 1500,
  6. VerticalAlignment = VerticalAlignment.Top,
  7. HorizontalAlignment = HorizontalAlignment.Left
  8. };
  9. PrintVisual(printer, visual);

3、监控打印任务状态

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

  1. public class PrintJobChecker
  2. {
  3. private DispatcherTimer _timer;
  4. private PrintQueue _printer;
  5.  
  6. private Action<string> _checkingAction;
  7.  
  8. public DateTime? StartPrintTime { get; set; }
  9.  
  10. private int _interval = 100;
  11. public int TimerInterval
  12. {
  13. get { return _interval; }
  14. set
  15. {
  16. _interval = value;
  17. _timer.Interval = TimeSpan.FromMilliseconds(value);
  18. }
  19. }
  20.  
  21. public PrintJobChecker(PrintQueue printer, Action<string> checkingAction)
  22. {
  23. if (printer == null || checkingAction == null)
  24. {
  25. return;
  26. }
  27.  
  28. _printer = printer;
  29. _checkingAction = checkingAction;
  30.  
  31. _timer = new DispatcherTimer
  32. {
  33. Interval = TimeSpan.FromMilliseconds(TimerInterval),
  34. };
  35.  
  36. _timer.Tick += CheckJobStatus;
  37.  
  38. PrintingStatus = "正在打印";
  39. PrintErrorStatus = "打印出错";
  40. PrintOfflineStatus = "请连接打印机";
  41. PrintWaittingStatus = "请放入相应的表单至打印机";
  42. PrintUnknownStatus = "未知错误";
  43. }
  44.  
  45. public void Start()
  46. {
  47. _timer.Start();
  48. }
  49.  
  50. public void Stop()
  51. {
  52. _timer.Stop();
  53. }
  54.  
  55. private void CheckJobStatus(object sender, EventArgs e)
  56. {
  57. if (_printer == null)
  58. {
  59. return;
  60. }
  61.  
  62. var job = _printer.GetLastJob();
  63. if (job == null)
  64. {
  65. if (!StartPrintTime.HasValue)
  66. {
  67. StartPrintTime = DateTime.Now;
  68. }
  69. _checkingAction(PrintingStatus);
  70. }
  71. else
  72. {
  73. var statusText = GetJobStatus(job);
  74. _checkingAction(statusText);
  75. }
  76. }
  77.  
  78. public string PrintingStatus { get; set; }
  79.  
  80. public string PrintErrorStatus { get; set; }
  81.  
  82. public string PrintOfflineStatus { get; set; }
  83.  
  84. public string PrintWaittingStatus { get; set; }
  85.  
  86. public string PrintUnknownStatus { get; set; }
  87.  
  88. private string GetJobStatus(PrintSystemJobInfo job)
  89. {
  90. if (job == null) return null;
  91.  
  92. if (((job.JobStatus & PrintJobStatus.Completed) == PrintJobStatus.Completed)
  93. ||
  94. ((job.JobStatus & PrintJobStatus.Printed) == PrintJobStatus.Printed))
  95. {
  96. StartPrintTime = DateTime.Now;
  97. return PrintingStatus;
  98. }
  99. if ((job.JobStatus & PrintJobStatus.Error) == PrintJobStatus.Error)
  100. {
  101. _timer.Stop();
  102. return PrintErrorStatus;
  103. }
  104. if ((job.JobStatus & PrintJobStatus.Offline) == PrintJobStatus.Offline
  105. ||
  106. job.JobStatus == PrintJobStatus.None)
  107. {
  108. return PrintOfflineStatus;
  109. }
  110. if ((job.JobStatus & PrintJobStatus.Printing) == PrintJobStatus.Printing)
  111. {
  112. if (job.TimeSinceStartedPrinting > 0)
  113. {
  114. return PrintingStatus;
  115. }
  116. else
  117. {
  118. return PrintWaittingStatus;
  119. }
  120. }
  121. return PrintUnknownStatus;
  122. }
  123. }

  

WPF打印票据的更多相关文章

  1. 洗衣店专用手持智能POS PDA手持设备 上门收衣 现场刷卡 打印票据 开单系统

    手持上门收衣设备通过wifi或者3G手机卡等进行联网,功能便捷强大,多功能一体同步使用,通过手持机上门收.取衣物,快速开单收衣消费.取货.新建会员.现场办理会员发卡.手持机读发会员卡和会员用卡消费等. ...

  2. WPF 打印实例

    原文:WPF 打印实例      在WPF 中可以通过PrintDialog 类方便的实现应用程序打印功能,本文将使用一个简单实例进行演示.首先在VS中编辑一个图形(如下图所示).      将需要打 ...

  3. WPF打印原理,自定义打印

    一.基础知识 1.System.Printing命名空间 我们可以先看一下System.Printing命名空间,东西其实很多,功能也非常强大,可以说能够控制打印的每一个细节,曾经对PrintDial ...

  4. WPF 打印控件 无弹框打印。

    WPF中打印用到了 PrintDialog类. 其中设置打印属性的是PrintTicket,管理打印机的是PrintQueue. 实例如下: public class PrintDialogHelpe ...

  5. C# WPF打印报表

    前天我的一个同学由于打印报表而苦恼,所以就介绍了一下WPF的打印报表,希望能帮助到大家. 展示报表 1. 首先新建项“报表”,选定项目,右击,点击“添加”->“新建项”->“报表”

  6. wpf打印控件 实现分页打印控件功能

    因为 要实现打印 wpf  listbox控件  数据特别多 要打印在 几张纸上    找了几天 都没有找到相关的例子 现在 解决了 在这里和大家分享一下 public void print(Fram ...

  7. [WPF打印]WPF 文档元素(Run TextBlock Paragraph)的文字对齐方式

    最近开发WPF程序,需要打印,用到了FlowDocument(这相当于有了打印模版,而且可以随时修改,真的是挺方便的).可是在输出表格形数据(这种情况恐怕是大多数~)时遇到了点儿麻烦. 由于Table ...

  8. WPF打印控件内容

    当我们想打印控件内容时,如一个Grid中的内容,可以用WPF中PrintDialog类的PrintVisual()方法来实现 界面如下: XAML代码如下 <Grid> <Grid. ...

  9. WPF 打印

    1. System.Windows.Controls.PrintDialog printDialog = new System.Windows.Controls.PrintDialog(); if ( ...

随机推荐

  1. [ALM]一步一步搭建MS ALM环境 - 安装域服务器

    描述: 搭建并配置域服务器,先安装操作系统,配置网络,安装组件,配置域帐号 步骤: 1,打开Hyper-V Manager,参考[Hyper-V]使用操作系统模板创建新的虚拟机,先完成操作系统的安装, ...

  2. [Hyper-V]在Windows 8.1 操作系统中启用Hyper-V功能

    描述: 如何在Windows 8.1 操作中启用Hyper-V功能 实现步骤: 1,安装Hyper-V 1 打开Control Panel,点击Progress 2 点击Turn Windows fe ...

  3. Hive的安装部署

    1.环境准备 1.1软件版本 hive-0.14 下载地址 2.配置 安装hive的前提,必需安装好hadoop环境,可以参考我之前Hadoop社区版搭建,先搭建好hadoop环境:接下来我们开始配置 ...

  4. Ubuntu命令--CURL用法

    curl命令是个功能强大的网络工具,支持通过http.ftp等方式下载文件.上传文件.还可以用来抓取网页.网络监控等方面的开发,解决开发过程中遇到的问题. 常用参数curl命令参数很多,这里只列出我曾 ...

  5. SpringMVC从一个controller跳转到另一个controller

    return "redirect:……路径……"; @RequestMapping(value = "/index", method = RequestMeth ...

  6. crossplatform---Nodejs in Visual Studio Code 10.IISNode

    1.开始 Nodejs in Visual Studio Code 08.IIS : http://www.cnblogs.com/mengkzhaoyun/p/5410185.html 参考此篇内容 ...

  7. atitit.js 各版本 and 新特性跟浏览器支持报告

    atitit.js 各版本 and 新特性跟浏览器支持报告 一个完整的JavaScript实现是由以下3个不同部分组成的 •核心(ECMAScript)--JavaScript的核心ECMAScrip ...

  8. eclipse 引用自己开发的模块

    这样就可以 生成的是LIB 工程需要设置“Is Library”

  9. CAN Timing Sample Point

    typedef struct { //char name[ 16 ]; // Name of the CAN controller hardware //uint32_t ref_clk; // CA ...

  10. 取代奶瓶Minidwep-gtk破解WPA 全攻略

    取代奶瓶Minidwep-gtk 破 WPA 全攻略  目录 1. CDlinux 下使用 minidwepgtk 获取握手包并使用自带的字典破解 2. 自带的字典破解不出密码时使用 U 盘外挂字典继 ...