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

如何为PrintDialog.PrintDocument传递文件名?文件冲突时追加时间戳

FlowDocument导出PDF:指定文件名+自动处理占用冲突

解决思路

PrintDialog.PrintDocument本身无法直接传入文件名,因为它依赖系统打印对话框完成交互。要实现自动指定文件名、遇到文件被打开时自动添加时间戳,需要绕开对话框手动配置打印队列,同时添加异常捕获逻辑处理文件占用冲突。

完整代码实现

using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Documents;
using System.Printing;

public void ExportFlowDocToPdf(FlowDocument flowDoc, string baseFileName = "My Document")
{
    // 获取系统内置的Microsoft Print to PDF虚拟打印机
    var printQueue = GetPdfPrintQueue();
    if (printQueue == null)
    {
        MessageBox.Show("未找到Microsoft Print to PDF打印机,请先启用该系统功能");
        return;
    }

    // 配置FlowDocument的打印尺寸匹配打印机可打印区域
    var printableArea = printQueue.GetPrintCapabilities().PageImageableArea;
    flowDoc.PageWidth = printableArea.ExtentWidth;
    flowDoc.PageHeight = printableArea.ExtentHeight;

    // 创建带页眉页脚的分页器(与原有代码逻辑一致)
    var paginator = new HeaderFooterPaginator<HeaderModel, FooterModel>(0, flowDoc, GetHeader(), GetFooter());

    string outputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"{baseFileName}.pdf");
    bool exportSuccess = false;

    while (!exportSuccess)
    {
        try
        {
            // 创建打印作业并指定输出文件路径
            var printJob = printQueue.AddJob(baseFileName, paginator, false);
            if (printJob is PrintSystemJobInfo jobInfo)
            {
                jobInfo.Properties["OutputFile"] = outputPath;
                jobInfo.Commit();
            }

            // 等待打印作业完成,避免提前结束导致文件损坏
            while (!printJob.IsCompleted && !printJob.IsFailed && !printJob.IsCanceled)
            {
                System.Threading.Thread.Sleep(100);
                printJob.Refresh();
            }

            if (printJob.IsCompleted)
            {
                exportSuccess = true;
                MessageBox.Show("PDF导出成功");
            }
            else
            {
                throw new Exception("打印作业失败或被取消");
            }
        }
        catch (IOException ex) when (ex.Message.Contains("被另一个进程使用"))
        {
            // 文件被占用时,自动生成带精确时间戳的新文件名
            string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
            outputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"{baseFileName}_{timestamp}.pdf");
            MessageBox.Show($"文件正在被使用,将改用文件名:{Path.GetFileName(outputPath)}");
        }
        catch (Exception ex)
        {
            MessageBox.Show($"导出失败:{ex.Message}");
            break;
        }
    }
}

// 封装获取Microsoft Print to PDF打印队列的方法
private PrintQueue GetPdfPrintQueue()
{
    var localPrintServer = new LocalPrintServer();
    return localPrintServer.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local, EnumeratedPrintQueueTypes.Connections })
                     .FirstOrDefault(q => q.Name.Equals("Microsoft Print to PDF", StringComparison.OrdinalIgnoreCase));
}

关键点说明

  • 绕开系统对话框:直接获取系统内置的PDF虚拟打印机,无需用户手动选择文件和打印机。
  • 自动处理文件冲突:捕获文件被占用的IOException,自动给文件名加上精确到秒的时间戳,避免重复文件名导致的崩溃。
  • 确保导出完整性:循环检查打印作业状态,等待PDF完全生成后再结束流程,防止文件损坏。

更简洁的替代方案(第三方库)

如果觉得系统打印机配置繁琐,可以使用第三方库直接转换,比如PdfSharp,代码逻辑更简单:

using PdfSharp.Xps;
using System.IO;
using System.Windows.Xps.Packaging;

public void ExportWithPdfSharp(FlowDocument flowDoc, string baseFileName)
{
    string outputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"{baseFileName}.pdf");
    try
    {
        // 先将FlowDocument转为XPS流,再转换为PDF
        using (var stream = new MemoryStream())
        {
            var xpsDoc = new XpsDocument(stream, FileAccess.ReadWrite);
            var xpsWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
            xpsWriter.Write(((IDocumentPaginatorSource)flowDoc).DocumentPaginator);
            stream.Position = 0;

            XpsConverter.Convert(stream, outputPath, XpsConverterOptions.None);
        }
        MessageBox.Show("导出成功");
    }
    catch (IOException ex) when (ex.Message.Contains("被另一个进程使用"))
    {
        // 生成带时间戳的新文件名并重试
        string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
        ExportWithPdfSharp(flowDoc, $"{baseFileName}_{timestamp}");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"导出失败:{ex.Message}");
    }
}

内容的提问来源于stack exchange,提问作者Harxish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:39:55