最近工做的內容是有關於WPF的,總體開發沒有什麼難度,主要是在打印上由於沒有任何經驗,犯了一些難,不過還好,解決起來也不是很費勁。spa
WPF打印票據或者是打印普通紙張區別不大,只是說打印票據要把須要打的內容擺放好位置,搞定縮放比例,就能夠放入票據直接打印了。
那麼關鍵點就是3個:
一、使用WPF提供的什麼類、什麼方法來執行打印
二、如何擺放位置
三、如何搞定縮放比例orm
這個問題很容易解決,搜索下WPF打印或WPF Print,就能找到示例代碼。
那麼我用的是PrintDialog的PrintVisual方法。PrintDialog從名字中能夠看出是個對話框,讓用戶手動選擇打印機。若是不想彈出對話框和選擇打印機,則能夠讀取默認打印機或者在配置文件裏配置打印機名稱,而後找到它。這就須要用到另外的兩個類:PrintQueue和LocalPrintServer。
使用PrintDialog打印:對象
var printDialog = new PrintDialog(); printDialog.PrintQueue = GetPrinter(); printDialog.PrintVisual(visual, visual.Name);
獲取打印機任務隊列:blog
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; } }
注意到咱們上面的打印代碼是使用的PrintVisual,參數是Visual,那麼這個Visual是什麼?
我舉個WPF Grid類的繼承關係:Grid : Panel : FrameworkElement : UIElement : Visual,因此WPF的控件都是繼承自UIElement的,也是繼承Visual的。
那麼咱們把Grid看做是一張票據或一張紙,在這張紙上佈置好須要打印的內容,不就OK了嗎。
你能夠建立一個用戶控件來鼠標拖拽擺放,傳入實體對象綁定值,也能夠動態生成一個Grid。繼承
僅僅擺放好,打印出來未必是咱們想要的結果。由於票據的大小不一樣,特別是銀行那種身份證或金額的小格子,打歪了只能說明技術不到家啊。
因此擺放是要有依據的,依據就是掃描票據,而後在掃描的底圖上擺放,樣位置就不會錯位。而後縮放就是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看看具體的類和方法的含義。開發
有些單據比較窄,可是寬度還能夠,因此但願能夠豎着打印,知足這個需求也是一句話的事情。
在visual.Measure(sz);語句以前增長下面兩行代碼便可。get
printDialog.PrintTicket.PageOrientation = PageOrientation.Landscape; printDialog.PrintTicket.PageMediaSize = new PageMediaSize(renderTarget.Height, renderTarget.Width);
退紙並非經常使用的功能,可是放錯了紙張想拿出來也要費一番力氣,因此想讓打印機自動吐出紙來。我也搜索了不少問答和文章,也沒試出來一個成功的,多是方法不正確。最終採用了一個比較雞賊的辦法,就是打印一個空白頁,而後自動退紙。每種針式打印機可能不一樣,因此退紙的空白頁的大小要調整好。string
var printer = GetPrinter(); var visual = new Grid() { Width = 1000, Height = 1500, VerticalAlignment = VerticalAlignment.Top, HorizontalAlignment = HorizontalAlignment.Left }; PrintVisual(printer, visual);
打印的時候確定想知道任務有沒有被打印,提醒用戶放入紙張,打印完畢後提醒用戶打印完成。我這裏寫了一個PrintJobChecker類,Start後就會根據timer的間隔時間檢查任務隊列,和打印時間。
可是.NET提供的方法並不能很好的作到理想的效果,只能獲取到任務還有沒有,這是很鬱悶的事情。一旦打印機開始打印(注意還沒完成),job就是null了。這沒法判斷紙張是否是還在打印中。若是有朋友知道怎麼處理還望評論告知。it
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; } }