• WPF打印票据


    最近工作的内容是有关于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;
    	}
    }
    

      

  • 相关阅读:
    Windows Python+Eclipse环境配置
    infobright系列二:数据迁移
    infobright系列一:源码安装infobright
    autotools归纳
    Atlas系列一:Atlas功能特点FAQ
    C#反射技术概念作用和要点
    .net获取本机公网IP代码
    Java泛型-类型擦除
    现在就使用HTML5的十大原因
    让网页图片变灰色的三种方法
  • 原文地址:https://www.cnblogs.com/mad/p/4594962.html
Copyright © 2020-2023  润新知