VS Visual Studio操作ReportViewer报表导出,多ReportViewer报表合并导出
前言
自VS2017版本后ReportViewer被摒弃,虽然可以通过NuGet再次安装回来(详见:VS Visual Studio添加ReportViewer控件(RDLC报表控件)),但是还是有很多API被摒弃,造成功能需求变窄,但是不得不说ReportViewer还是有其存在的必要性,下面的内容用来介绍如何导出ReportViewer为PDF,多个ReportViewer文件导出到同一个PDF文件,当然我们这里的ReportViewer报表并不是表格类型的,如下图:
解决方案
01、安装iTextSharp,用于操作PDF文件(这里主要是用来合并)
安装命令(也可使用NuGet安装包管理器搜索安装)
dotnet add package iTextSharp --version [具体版本号]
02、先定义报表文件和报表文件的数据集(这里是按对象连接的)
对象代码
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RdlcModel { public class SplitCode { /// <summary> /// 批号 /// </summary> public string BatchNumber { get; set; } /// <summary> /// 有效期 /// </summary> public string ValidToDate { get; set; } /// <summary> /// 分装日期 /// </summary> public string SysDate { get; set; } /// <summary> /// 追溯码拆零码 /// </summary> public string SplitTraceCode { get; set; } /// <summary> /// 追溯码拆零码图像 /// </summary> public byte[] SplitTraceCodeImg { get; set; } /// <summary> /// 拆零比 /// </summary> public string SplitRatio { get; set; } } }
03、报表文件绑定
04、实例化对象并赋值ReportViewer,这里注意不能直接把对象赋值ReportViewer,会报错,代码如下:
//刷新,清空 this.reportViewer1.RefreshReport(); this.reportViewer1.LocalReport.DataSources.Clear(); //创建对象List<SplitCode> List<SplitCode> splitCodes = new List<SplitCode>(); //创建SplitCode对象 SplitCode splitCode = new SplitCode(); //赋值 splitCode.ValidToDate = "2024-12-31"; splitCode.SysDate = DateTime.Today.ToString("yyyy-MM-dd"); splitCode.SplitRatio = "10/24"; splitCode.BatchNumber = "PH00025"; splitCode.SplitTraceCode = "0137744338578002"; splitCode.SplitTraceCodeImg = code128.GetCode128("83437270137744338578001"); //这里的code128是用来生成一维码的方法 //SplitCode对象添加到List<SplitCode> splitCodes.Add(splitCode); //将List<SplitCode>对象绑定到RDLC文件,这里DataSetSplitCode是RDLC中的名称 var RdlcModelSplitCode = new Microsoft.Reporting.WinForms.ReportDataSource("DataSetSplitCode", splitCodes); //将RdlcModelSplitCode绑定到reportViewer1控件 this.reportViewer1.LocalReport.DataSources.Add(RdlcModelSplitCode); //刷新控件 this.reportViewer1.RefreshReport();
05、创建ReportViewer导出PDF的方法
/// <summary> /// ReportViewer导出为PF文件 /// </summary> /// <param name="reportViewer"></param> public void ExportReportToPdf(ReportViewer reportViewer) { // 确保 ReportViewer 已加载报告 if (reportViewer.LocalReport.ReportPath == string.Empty) { MessageBox.Show("请先加载报告。"); return; } // 设置导出参数 Warning[] warnings; string[] streamIDs; string mimeType; string encoding; string extension; // 获取临时文件路径 string tempFilePath = Path.GetTempFileName(); // 更改文件扩展名为 .pdf tempFilePath = Path.ChangeExtension(tempFilePath, ".pdf"); // 自定义的页面设置(例如A4纸大小)IN寸转CM 2.54CM string deviceInfo = @"<DeviceInfo> <OutputFormat>PDF</OutputFormat> <PageWidth>2.2in</PageWidth> <PageHeight>3.0in</PageHeight> <MarginTop>0.1in</MarginTop> <MarginLeft>0.1in</MarginLeft> <MarginRight>0.1in</MarginRight> <MarginBottom>0.1in</MarginBottom> </DeviceInfo>"; // 导出报告为byte[] 字节数组 byte[] bytes = reportViewer.LocalReport.Render( "PDF", // 格式 deviceInfo, // 设备信息(对于 PDF,通常为 null) out mimeType, out encoding, out extension, out streamIDs, out warnings); // 将byte[]字节数组写入文件 File.WriteAllBytes(tempFilePath, bytes); //记录临时文件路径 PathStrs.Add(tempFilePath); }
使用方法
ExportReportToPdf(this.reportViewer1);
上述方法中PathStrs.Add(tempFilePath)是用来记录导出的pdf目录地址,是一个List<string> 对象:
List<string> PathStrs = new List<string>();
注意:this.reportViewer1的值,赋值给List<ReportViewer>对象,是把控件的(内存地址?)赋值给List<ReportViewer>的元素,故无论怎么操作List<ReportViewer>的值均为最后一个值。
06、使用foreach去循环导出不同对象的PDF文件,并记录在PathStrs对象中
List<SplitCode> splitCodes = new List<SplitCode>(); Code128 code128 = new Code128(); //创建reportViewer1对象的List<SplitCode> SplitCode splitCode = new SplitCode(); splitCode.ValidToDate = "2024-12-31"; splitCode.SysDate = DateTime.Today.ToString("yyyy-MM-dd"); splitCode.SplitRatio = "10/24"; splitCode.BatchNumber = "PH00025"; splitCode.SplitTraceCode = "0137744338578001"; splitCode.SplitTraceCodeImg = code128.GetCode128("83437270137744338578001"); splitCodes.Add(splitCode); SplitCode splitCode1 = new SplitCode(); splitCode1.ValidToDate = "2025-12-31"; splitCode1.SysDate = DateTime.Today.ToString("yyyy-MM-dd"); splitCode1.SplitRatio = "5/24"; splitCode1.BatchNumber = "PH00005"; splitCode1.SplitTraceCode = "0137744338578002"; splitCode1.SplitTraceCodeImg = code128.GetCode128("83437270137744338578002"); splitCodes.Add(splitCode1); //循环遍历List<SplitCode>对象splitCodes foreach (var item in splitCodes) { List<SplitCode> splitCodeslog = new List<SplitCode> { item }; this.reportViewer1.RefreshReport(); this.reportViewer1.LocalReport.DataSources.Clear(); this.reportViewer1.LocalReport.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource("DataSetSplitCode", splitCodeslog)); this.reportViewer1.RefreshReport(); ExportReportToPdf(this.reportViewer1); }
07、合并PDF的方法
/// <summary> /// 合并多个PDF文单独PDF文件 /// </summary> /// <param name="reportViewers"></param> /// <param name="outputPath"></param> private void MergeReportsToPdf(string outputPath) { using (FileStream stream = new FileStream(outputPath, FileMode.Create)) { Document document = new Document(); PdfCopy pdfCopy = new PdfCopy(document, stream); PdfReader reader = null; try { document.Open(); foreach (var PathStr in PathStrs) { // 打开临时PDF文件 reader = new PdfReader(PathStr); // 获取PDF文件页数 int numberOfPages = reader.NumberOfPages; // 主文件循环添加PDF页面 for (int page = 0; page < numberOfPages;) { pdfCopy.AddPage(pdfCopy.GetImportedPage(reader, ++page)); } // 关闭当前文件,这里删除会报错,所以删除临时文件放在最后 reader.Close(); } } catch (Exception ex) { MessageBox.Show("出现错误: " + ex.Message); } finally { if (reader != null) { reader.Close(); } if (document.IsOpen()) { document.Close(); //循环删除临时文件 foreach (var PathStr in PathStrs) { File.Delete(PathStr); } //打开汇总文件 System.Diagnostics.Process.Start(outputPath); } } } }
使用方法
string outputPath = "ReportViews.pdf"; MergeReportsToPdf(outputPath); MessageBox.Show("报告已成功合并到" + outputPath);