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;
	}
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM