.NET MAUI POS系统实现PDF无弹窗打印需求
.NET MAUI POS系统无弹窗打印PDF实现方案
核心思路
先检测系统是否存在默认打印机:
- 存在则直接静默打印PDF,不弹出窗口
- 不存在则弹出打印机选择对话框
由于MAUI是跨平台框架,需针对Windows、Android、iOS/macOS分别实现打印逻辑,以下是具体步骤和代码:
1. 定义通用打印服务接口
通过依赖注入实现跨平台服务隔离,定义接口统一打印操作:
public interface IPdfPrintService { // 检测是否存在默认打印机 Task<bool> HasDefaultPrinterAsync(); // 静默打印PDF Task PrintPdfSilentlyAsync(string pdfFilePath); // 弹出打印机选择对话框 Task ShowPrintSelectionDialogAsync(string pdfFilePath); }
2. 各平台实现打印服务
Windows平台
Windows原生支持直接获取默认打印机并静默打印,使用System.Printing命名空间:
#if WINDOWS using System.Printing; using Microsoft.Maui.Graphics; public class WindowsPdfPrintService : IPdfPrintService { public async Task<bool> HasDefaultPrinterAsync() { return await Task.Run(() => { using var printServer = new LocalPrintServer(); return printServer.DefaultPrintQueue != null; }); } public async Task PrintPdfSilentlyAsync(string pdfFilePath) { await Task.Run(() => { using var printServer = new LocalPrintServer(); var defaultQueue = printServer.DefaultPrintQueue; // 添加打印任务,第三个参数设为false表示静默提交 var printJob = defaultQueue.AddJob("POS收据", pdfFilePath, false); // 可选:监听打印任务状态 printJob.JobStatusChanged += (s, e) => { if (printJob.JobStatus.HasFlag(PrintJobStatus.Completed)) { // 处理打印完成逻辑 } }; }); } public async Task ShowPrintSelectionDialogAsync(string pdfFilePath) { var printManager = Microsoft.Maui.Graphics.Printing.PrintManager.Default; var document = await PdfDocument.LoadFromPathAsync(pdfFilePath); await printManager.PrintAsync(document, "POS收据"); } } #endif
Android平台
Android系统限制普通应用实现完全静默打印,若为常规打印机需弹出系统打印界面;若为小票打印机,建议使用厂商SDK直接发送打印指令(如ESC/POS):
#if ANDROID using Android.Content; using Android.Print; using Java.IO; public class AndroidPdfPrintService : IPdfPrintService { public async Task<bool> HasDefaultPrinterAsync() { return await Task.Run(() => { var printManager = Android.App.Application.Context.GetSystemService(Context.PrintService) as PrintManager; // 检测是否有可用打印机 var printers = printManager.GetPrintServices(PrintServiceFilter.All); return printers.Count > 0; }); } public async Task PrintPdfSilentlyAsync(string pdfFilePath) { // Android普通应用无法完全静默打印,直接调用对话框打印 await ShowPrintSelectionDialogAsync(pdfFilePath); } public async Task ShowPrintSelectionDialogAsync(string pdfFilePath) { var printManager = Android.App.Application.Context.GetSystemService(Context.PrintService) as PrintManager; var jobName = "POS收据"; var file = new File(pdfFilePath); printManager.Print(jobName, new PdfDocumentAdapter(jobName, file), new PrintAttributes.Builder().Build()); } // 自定义PDF打印适配器 private class PdfDocumentAdapter : PrintDocumentAdapter { private readonly string _jobName; private readonly File _pdfFile; public PdfDocumentAdapter(string jobName, File pdfFile) { _jobName = jobName; _pdfFile = pdfFile; } public override void OnLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) { if (cancellationSignal.IsCanceled) { callback.OnLayoutCancelled(); return; } var docInfo = new PrintDocumentInfo.Builder(_jobName) .SetContentType(PrintDocumentInfo.ContentTypeDocument) .SetPageCount(PrintDocumentInfo.PageCountUnknown) .Build(); callback.OnLayoutFinished(docInfo, true); } public override void OnWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) { if (cancellationSignal.IsCanceled) { callback.OnWriteCancelled(); return; } try { using var input = new FileInputStream(_pdfFile); using var output = new FileOutputStream(destination.FileDescriptor); byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = input.Read(buffer)) != -1) { output.Write(buffer, 0, bytesRead); } callback.OnWriteFinished(new PageRange[] { PageRange.AllPages }); } catch (Exception e) { callback.OnWriteFailed(e.Message); } } } } #endif
iOS/macOS平台
iOS 14+支持获取默认打印机并静默打印,低于该版本只能弹出打印对话框:
#if IOS || MACCATALYST using UIKit; using Foundation; public class ApplePdfPrintService : IPdfPrintService { public async Task<bool> HasDefaultPrinterAsync() { return await Task.Run(() => { // iOS14及以上支持获取默认打印机 if (UIDevice.CurrentDevice.CheckSystemVersion(14, 0)) { return UIPrintInteractionController.Shared.DefaultPrinter != null; } return false; }); } public async Task PrintPdfSilentlyAsync(string pdfFilePath) { await Task.Run(() => { if (UIDevice.CurrentDevice.CheckSystemVersion(14, 0)) { var printer = UIPrintInteractionController.Shared.DefaultPrinter; var printInfo = UIPrintInfo.PrintInfo; printInfo.JobName = "POS收据"; printInfo.OutputType = UIPrintInfoOutputType.General; var printController = UIPrintInteractionController.Shared; printController.PrintInfo = printInfo; printController.PrintingItem = NSUrl.FromFilename(pdfFilePath); // 直接发送到默认打印机 printController.PrintToPrinter(printer, (controller, completed, error) => { if (error != null) { // 处理打印错误 } }); } else { // iOS14以下无法静默打印,弹出对话框 ShowPrintSelectionDialogAsync(pdfFilePath).Wait(); } }); } public async Task ShowPrintSelectionDialogAsync(string pdfFilePath) { await Task.Run(() => { var printInfo = UIPrintInfo.PrintInfo; printInfo.JobName = "POS收据"; printInfo.OutputType = UIPrintInfoOutputType.General; var printController = UIPrintInteractionController.Shared; printController.PrintInfo = printInfo; printController.PrintingItem = NSUrl.FromFilename(pdfFilePath); printController.Present(true, (controller, completed, error) => { if (error != null) { // 处理错误 } }); }); } } #endif
3. 注册服务并使用
在MauiProgram.cs中注册跨平台服务:
builder.Services.AddSingleton<IPdfPrintService>(sp => { #if WINDOWS return new WindowsPdfPrintService(); #elif ANDROID return new AndroidPdfPrintService(); #elif IOS || MACCATALYST return new ApplePdfPrintService(); #else throw new NotSupportedException("当前平台不支持打印功能"); #endif });
在打印按钮点击事件中调用服务:
private readonly IPdfPrintService _printService; // 通过构造函数注入服务 public ReceiptPage(IPdfPrintService printService) { InitializeComponent(); _printService = printService; } private async void PrintBtn_Clicked(object sender, EventArgs e) { // 替换为实际生成的PDF文件路径 string pdfPath = Path.Combine(FileSystem.AppDataDirectory, "receipt.pdf"); bool hasDefaultPrinter = await _printService.HasDefaultPrinterAsync(); if (hasDefaultPrinter) { await _printService.PrintPdfSilentlyAsync(pdfPath); } else { await _printService.ShowPrintSelectionDialogAsync(pdfPath); } }
注意事项
- Windows:需确保应用有打印机访问权限,可在
Package.appxmanifest中添加打印相关权限。 - Android:若需小票打印机静默打印,建议使用厂商提供的SDK或直接发送ESC/POS指令到蓝牙/USB设备。
- iOS/macOS:仅iOS14+支持静默打印,低版本需依赖系统打印对话框。
- PDF路径:使用
FileSystem.AppDataDirectory存储生成的PDF,确保各平台可访问。
内容的提问来源于stack exchange,提问作者Danul Hivi Vihan
相关产品推荐
相关产品推荐

