在WPF项目中实现打印预览、预览PDF文件

作者: harde 分类: 我的笔记 发布时间: 2025-01-15 17:06

这段时间在给甲方客户重构一款桌面软件。原程序是用WPF在 .NET Framework 4.7.2 下开发的。
本次重构,放弃了.NET Framework ,改为.NET 8.0。 一路坎坎坷坷就不说了。打印预览卡住了很久。
原有程序打印预览使用了PrintDialogX(https://github.com/Fei-Sheng-Wu/PrintDialogX),这个组件本身没有太大问题。
问题出在项目本身,因为打印预览的是每秒的设备信息,每次打印的数据大概有2个小时,7200多条数据,原来的逻辑是把数据渲染在WPF组件上,然后放到PrintDialogX里,打开一次预览,能卡到崩溃。
于是打算换一个思路,因为本身是纯Windows,首先想到xps,生成xps后,用DocumentViwer预览,效果也是非常好。但是经过尝试,生成XPS效率也是非常低下。(生成XPS的方案放在文章后面,感兴趣的可以看看),尤其是打印还有几个图表,效率更是低下。

于是放弃了XPS的方案,想着反正也需要做导出PDF的功能,不如直接先生成PDF,然后把PDF嵌入到WPF里。

想法是好的,实施起来又困难重重,首先生成PDF不是什么问题,使用NPOI(https://github.com/tonyqus/npoi)生成效率没得说,但是生成后放到WPF里却犯了难。

能找到的方案,基本都是pdfium,把PDF转成图片,还不如XPS。后来想到,不如走浏览器的方案。用WebBrower加载pdf.js来预览PDF,经过实际测试,如果pdf.js 放在本地,会遭遇浏览器的安全问题,为了这个事,内建一个Web服务我觉得也不是明智之举,正在苦恼的时候,突然想到本身Chrome、Edge这些Chromium内核的浏览器就是支持PDF浏览的,为什么我要多此一举套一层pdf.js呢,于是给项目安装了webview2组件(Microsoft.Web.Webview2),直接用webview2加载pdf文件。效果完美。


<webview2:WebView2 Name="pdfWebViewer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

加载PDF时,注意一下,需要等待WebView2加载完毕,可以在构造方法里,调用以下样例。

    private async void InitializeWebView2(string tempFile)
    {
        // 确保 WebView2 已经初始化
        await pdfWebViewer.EnsureCoreWebView2Async();


        // 将 HTML 内容加载到 WebBrowser 控件
        // pdfWebViewer.NavigateToString(htmlContent);
        // pdfWebViewer.Navigate(pdfJsPath);
        // 将 HTML 内容加载到 WebView2 控件
        pdfWebViewer.CoreWebView2.Navigate(tempFile);

    }

以下为用xaml文件生成XPS的代码。


<FixedPage  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            Width="1123" Height="794">

        <Grid Background="White" VerticalAlignment="Top" HorizontalAlignment="Left" Height="1500" Width="1123">
        <Label Content="打印时间:" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Margin="700,56,0,0"/>
        <Label x:Name="LblPrintedAt" Content="打印时间" HorizontalAlignment="Left" VerticalAlignment="Top" Width="300" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" Margin="800,56,0,0"/>

    </Grid>
</FixedPage>
       //要生成的xps文件路径
        string outputXpsFile = Path.GetTempFileName() + ".xps";

        // 创建新生成的XPS文件
        using (XpsDocument xpsDocument = new XpsDocument(outputXpsFile, FileAccess.ReadWrite))
        {
            XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);

            // 创建 新的FixedDocument
            FixedDocument newDoc = new FixedDocument();
            // 创建新的 PageContent
            PageContent pageContent = new PageContent();
            // 添加到文档
            newDoc.Pages.Add(pageContent);

            // 读取 模板 文件并将其转换为 FixedPage
            using (FileStream stream = new FileStream(templatePath, FileMode.Open))
            {
                // 加载 XAML 内容
                FixedPage headerPage = (FixedPage) XamlReader.Load(stream);

                pageContent.Child = headerPage;
                // 写入数据
                // 打印时间
                Label lblPrintedAt = headerPage.FindName("LblPrintedAt") as Label;
                lblPrintedAt.Content = DateTime.Now.ToString(CommonClass.DateFormat);
            }

            // 写入 XPS 文件
            writer.Write(newDoc);
        }

⬆️⬆️⬆️不要钱、不用登录、没有广告⬆️⬆️⬆️


如果文章对您有帮助,请至少点下⬆️⬆️⬆️上面⬆️⬆️⬆️的赞,您的鼓励是对我创作的最大支持。
如果没能帮到您,那么很遗憾,如果方便的话,还请在评论区告诉我为什么,谢谢。

⬇️⬇️⬇️下面这个要钱⬇️⬇️⬇️

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注