Blazor Server中DevExpress控件表单自定义页眉页脚PDF打印方案咨询
可行方案汇总
方案1:利用DevExpress内置导出API(推荐,适配性最好)
既然你用的是DevExpress控件,官方自带的PDF导出与打印预览能力完全适配自身控件渲染,能精准控制内容范围:
- 单个控件导出:DxChart、DxDashboard这类可视化控件直接调用
ExportToPdfAsync方法,可指定页眉页脚。以饼图为例:var stream = new MemoryStream(); await PieChart.ExportToPdfAsync(stream, new PdfExportOptions { Header = new PdfExportHeaderFooter { Text = "仪表详情报表" }, Footer = new PdfExportHeaderFooter { Text = $"打印日期:{DateTime.Now:yyyy-MM-dd}" } }); // 后续可将stream转为文件,供前端预览或下载 - 整表单导出:用DevExpress的
PrintingSystem组件手动构建打印文档,自由添加表单文本、饼图等元素,完全自定义布局与页眉页脚,还能通过DxPrintPreview控件渲染预览:var printingSystem = new PrintingSystem(); var document = printingSystem.CreateDocument(); // 添加表单文本内容 var textBrick = document.BeginCreateBrick<TextBrick>(); textBrick.Text = $"仪表编号:{Instrument.Id}"; textBrick.Rect = new RectangleF(0, 0, 500, 20); document.EndCreateBrick(textBrick); // 将饼图转为图片添加到文档 var chartImage = await PieChart.ExportToImageAsync(); var imageBrick = document.BeginCreateBrick<ImageBrick>(); imageBrick.Image = chartImage; imageBrick.Rect = new RectangleF(0, 30, 500, 300); document.EndCreateBrick(imageBrick); // 设置全局页眉页脚 printingSystem.PageSettings.Header.Content.Add("自定义页眉"); printingSystem.PageSettings.Footer.Content.Add("第 [Page] 页,共 [Pages] 页"); // 绑定到预览组件显示 PrintPreviewComponent.PrintingSystem = printingSystem;
方案2:前端HTML转PDF(无后端依赖)
用jsPDF+html2canvas组合精准捕获目标表单区域,临时隐藏侧边栏等无关元素,同时自定义页眉页脚:
- 下载jsPDF和html2canvas的脚本文件到项目
wwwroot/js目录,在Blazor组件中引入:<script src="~/js/jspdf.umd.min.js"></script> <script src="~/js/html2canvas.min.js"></script> - 编写JS交互函数(通过IJSRuntime调用):
async function exportFormToPdf(formId) { // 临时隐藏侧边栏等不需要的元素 const sidebar = document.querySelector('.sidebar'); const originalDisplay = sidebar.style.display; sidebar.style.display = 'none'; const formElement = document.getElementById(formId); const canvas = await html2canvas(formElement, { scale: 2, // 提升导出清晰度 useCORS: true }); // 恢复侧边栏显示 sidebar.style.display = originalDisplay; const pdf = new jspdf.jsPDF('p', 'mm', 'a4'); const imgWidth = 210; const pageHeight = 297; const imgHeight = canvas.height * imgWidth / canvas.width; let heightLeft = imgHeight; let position = 0; // 添加表单内容到PDF pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; // 处理分页(内容过长时自动分页) while (heightLeft >= 0) { position = heightLeft - imgHeight; pdf.addPage(); pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; } // 添加自定义页眉页脚 const pageCount = pdf.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { pdf.setPage(i); // 页眉居中显示 pdf.text('仪表详情报表', 105, 10, { align: 'center' }); // 页脚居中显示页码 pdf.text(`第 ${i} 页 / 共 ${pageCount} 页`, 105, 290, { align: 'center' }); } // 触发打印预览或直接下载 pdf.autoPrint(); // 打开浏览器打印预览窗口 // 或使用 pdf.save('仪表报表.pdf'); 直接下载 } - 在Blazor组件中调用该JS函数:
[Inject] private IJSRuntime JsRuntime { get; set; } private async Task ExportFormPdf() { // 传入目标表单的ID await JsRuntime.InvokeVoidAsync("exportFormToPdf", "target-instrument-form"); }
方案3:后端生成PDF(完全可控布局)
如果需要极致的布局控制,可在Blazor Server后端生成PDF,推荐使用DinkToPdf(基于wkhtmltopdf):
- 安装NuGet包:
DinkToPdf - 后端编写PDF生成逻辑,接收表单数据与饼图Base64图片,构建HTML模板转PDF:
public byte[] GenerateInstrumentPdf(InstrumentDetails details, string chartImageBase64) { var converter = new SynchronizedConverter(new PdfTools()); var pdfDoc = new HtmlToPdfDocument() { GlobalSettings = { PaperSize = PaperKind.A4, Margins = new MarginSettings { Top = 20, Bottom = 20 }, Out = null // 返回字节数组而非保存到文件 }, Objects = { new ObjectSettings { HtmlContent = $@" <html> <head> <style> .form-wrapper {{ width: 90%; margin: 0 auto; }} .chart-img {{ width: 100%; max-height: 350px; margin: 20px 0; }} .footer {{ position: fixed; bottom: 10px; width: 100%; text-align: center; font-size: 12px; }} </style> </head> <body> <!-- 自定义页眉 --> <h2 style='text-align: center; margin-bottom: 30px;'>仪表详情报表</h2> <!-- 表单内容 --> <div class='form-wrapper'> <p><strong>仪表编号:</strong>{details.Id}</p> <p><strong>仪表名称:</strong>{details.Name}</p> <p><strong>状态:</strong>{details.Status}</p> <!-- 饼图 --> <img class='chart-img' src='data:image/png;base64,{chartImageBase64}' /> </div> <!-- 自定义页脚 --> <div class='footer'> 打印日期:{DateTime.Now:yyyy-MM-dd HH:mm} | 第 [page] 页 / 共 [toPage] 页 </div> </body> </html>" } } }; return converter.Convert(pdfDoc); } - 前端调用后端接口,获取PDF字节流后实现预览:
对应的JS预览函数(放在wwwroot/js中):[Inject] private IJSRuntime JsRuntime { get; set; } [Inject] private InstrumentService Service { get; set; } private async Task PreviewPdf() { // 将饼图转为Base64图片 var chartImage = await PieChart.ExportToImageAsync(); var chartBase64 = ConvertImageToBase64(chartImage); // 调用后端生成PDF var pdfBytes = await Service.GenerateInstrumentPdf(CurrentInstrumentDetails, chartBase64); // 前端预览PDF await JsRuntime.InvokeVoidAsync("previewPdf", Convert.ToBase64String(pdfBytes)); } // 辅助方法:Image转Base64 private string ConvertImageToBase64(Image image) { using var ms = new MemoryStream(); image.Save(ms, ImageFormat.Png); return Convert.ToBase64String(ms.ToArray()); }function previewPdf(base64Pdf) { const byteCharacters = atob(base64Pdf); const byteNumbers = new Uint8Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } const blob = new Blob([byteNumbers], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); window.open(url, '_blank'); // 打开新窗口预览PDF URL.revokeObjectURL(url); // 释放URL资源 }
内容的提问来源于stack exchange,提问作者RachChandra
相关产品推荐
相关产品推荐

