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);

