在WPF项目中实现打印预览、预览PDF文件
这段时间在给甲方客户重构一款桌面软件。原程序是用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);
}