WPF打印票據

最近工做的內容是有關於WPF的,總體開發沒有什麼難度,主要是在打印上由於沒有任何經驗,犯了一些難,不過還好,解決起來也不是很費勁。spa

WPF打印票據或者是打印普通紙張區別不大,只是說打印票據要把須要打的內容擺放好位置,搞定縮放比例,就能夠放入票據直接打印了。
那麼關鍵點就是3個:
一、使用WPF提供的什麼類、什麼方法來執行打印
二、如何擺放位置
三、如何搞定縮放比例orm

一、使用WPF提供的什麼類、什麼方法來執行打印

這個問題很容易解決,搜索下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;
	}
}
相關文章
相關標籤/搜索