You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 08:55:19