使用ReportViewer生成动态报告----项目应用心得(未完待续)
看图,这是一个人才测评报告,报告中包含多个子部分,部分的个数,内容都是变化的。
所以子报告部分我们采用子报表来实现。
下面讲解一下构建一个这样的报告会遇到的关键问题,并且提供方案方法。
问题一。 如果报告中子报告的数量和报告源都是不确定的,如何呈现?
按照我们一般的思路,就考虑建立一个Tablix表格,绑定一组数据源,然后在里面放置子报告。
![](https://images.cnblogs.com/cnblogs_com/dnncenter/%E5%AD%90%E6%8A%A5%E5%91%8A%E5%B5%8C%E5%A5%97%E5%9C%A8table%E4%B8%AD.jpg)
不过很遗憾,这样并不能实现, 子报告的报告源必须是定值,不能传递参数或者绑定数据源。。 这下麻烦了,那如果做了?
首先呢,参考这个
地址
http://www.gotreportviewer.com/
里面有 Generate RDLC dynamically - Table 和 Generate RDLC dynamically - Matrix
可以找到
然后下载此文件, 在
就可以得到ReportDefinition的实体类了。 方便你动态的创建报告文件。
通过这两个范例, 就有思路了, 我们可以把主报表动态创建出来, 把子报表以代码的方式动态的追加写入主报告文件
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace VAP.Modules.CP.ActiveResult.Rdl
{
/// <summary>
/// 子报表创建类
/// </summary>
public class SubReportRdlGenerator
{
/// <summary>
/// 创建一个子报表
/// </summary>
/// <param name="strreportname">子报告名</param>
/// <param name="strsubreport">子报表文件路径</param>
/// <param name="paras">参数数组</param>
/// <param name="inttop">创建位置(高度)</param>
/// <returns></returns>
public Rdl.SubreportType CreateSubReport(string strreportname,string strsubreport,Rdl.ParametersType paras,double inttop)
{
Rdl.SubreportType subreport = new Rdl.SubreportType();
subreport.Name = strreportname;
//subreport.Items=;
subreport.Items = new object[] { strsubreport, paras, "8cm", "8cm" ,inttop.ToString()+"cm","0.4cm"};
subreport.ItemsElementName = new Rdl.ItemsChoiceType16[]
{
Rdl.ItemsChoiceType16.ReportName,
Rdl.ItemsChoiceType16.Parameters,
Rdl.ItemsChoiceType16.Width,
Rdl.ItemsChoiceType16.Height,
Rdl.ItemsChoiceType16.Top,
Rdl.ItemsChoiceType16.Left
};
return subreport;
//return matrix;
}
public Rdl.ParametersType CreateParameters()
{
Rdl.ParametersType para = new ParametersType();
//para.Parameter[0].
return para;
}
}
}
报告创建类, 这个类是我写的,并不通用,请根据自己的情况创建, 这里有大量的针对我的表的参数
using System.Data;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Data.SqlClient;
using Microsoft.ApplicationBlocks.Data;
namespace VAP.Modules.CP.ActiveResult.Rdl
{
public class RdlGenerator
{
private Rdl.Report _report;
private List<object> lstsubobjects = new List<object>();
private int intSubReports = 0;
private List<string> m_allFields;
private double intEndTop = 0;
public List<string> AllFields
{
get { return m_allFields; }
set { m_allFields = value; }
}
#region 创建报表
public Rdl.Report CreateReport()
{
Rdl.DataSetGenerator datasetgenerator = new DataSetGenerator("ExportUserField", "LergerDataSet", "CP_ExportUserField");
datasetgenerator.AllFields = AllFields;
Rdl.Report report = new Rdl.Report();
report.Items = new object[]
{
CreateDataSources(),
CreateBody(),
datasetgenerator.CreateDataSets(),
"0in",
};
report.ItemsElementName = new Rdl.ItemsChoiceType80[]
{
Rdl.ItemsChoiceType80.DataSources,
Rdl.ItemsChoiceType80.Body,
Rdl.ItemsChoiceType80.DataSets,
Rdl.ItemsChoiceType80.Width,
};
_report = report;
return report;
}
private Rdl.DataSourcesType CreateDataSources()
{
Rdl.DataSourcesType dataSources = new Rdl.DataSourcesType();
dataSources.DataSource = new Rdl.DataSourceType[] { CreateDataSource() };
return dataSources;
}
private Rdl.DataSourceType CreateDataSource()
{
Rdl.DataSourceType dataSource = new Rdl.DataSourceType();
dataSource.Name = "LergerDataSet";
dataSource.Items = new object[] { CreateConnectionProperties() };
return dataSource;
}
private Rdl.ConnectionPropertiesType CreateConnectionProperties()
{
Rdl.ConnectionPropertiesType connectionProperties = new Rdl.ConnectionPropertiesType();
connectionProperties.Items = new object[]
{
"/* Local Connection */",
"System.Data.DataSet",
};
connectionProperties.ItemsElementName = new Rdl.ItemsChoiceType[]
{
Rdl.ItemsChoiceType.ConnectString,
Rdl.ItemsChoiceType.DataProvider,
};
return connectionProperties;
}
private Rdl.BodyType CreateBody()
{
Rdl.BodyType body = new Rdl.BodyType();
body.Items = new object[]
{
CreateReportItems(),
};
return body;
}
private Rdl.ReportItemsType CreateReportItems()
{
Rdl.ReportItemsType reportItems = new Rdl.ReportItemsType();
reportItems.Items = lstsubobjects.ToArray();
return reportItems;
}
#endregion
#region 加载报表
public Rdl.Report LoadReport(string strreportfile,double intendtop)
{
XmlSerializer serializer = new XmlSerializer(typeof(Rdl.Report));
Stream reader = new FileStream(strreportfile, FileMode.Open);
_report = (Rdl.Report)serializer.Deserialize(reader);
reader.Close();
intEndTop = intendtop;
return _report;
}
#endregion
public void LoadSubReports()
{
AddReportItems(lstsubobjects);
}
private void AddReportItems(List<object> lstobjects)
{
List<object> lstmain = new List<object>();
Rdl.BodyType body = _report.Items[2] as Rdl.BodyType;
Rdl.ReportItemsType reportItems = body.Items[0] as Rdl.ReportItemsType;
for (int i = 0; i < reportItems.Items.Length; i++)
{
lstmain.Add(reportItems.Items[i]);
}
lstmain.AddRange(lstobjects);
reportItems.Items = lstmain.ToArray();
}
private void AddReportItem(object reportitem)
{
}
private Rdl.PageBreakType CreatePageBreakType()
{
Rdl.PageBreakType pagebreak = new PageBreakType();
pagebreak.Items = new object[] { PageBreakTypeBreakLocation.Start };
return pagebreak;
}
public void CreateSubreport(string strreportname, string strsubreport, string strBlockID, string strBlockName)
{
double intt = intEndTop + intSubReports * 8;
RectangleType rectype = new RectangleType();
rectype.Name = "rect"+strBlockName;
rectype.Items = new object[] { CreatePageBreakType(), intt.ToString() + "cm","0.1cm" };
rectype.ItemsElementName = new ItemsChoiceType10[] { Rdl.ItemsChoiceType10.PageBreak,Rdl.ItemsChoiceType10.Top,Rdl.ItemsChoiceType10.Height};
lstsubobjects.Add(rectype);
intt = intEndTop + intSubReports * 8;
SubReportRdlGenerator subreportgen = new SubReportRdlGenerator();
Rdl.ParametersType paras = new ParametersType();
Rdl.ParameterType parablockid = new ParameterType();
parablockid.Name = "BlockID";
parablockid.Items = new object[] { strBlockID };
parablockid.ItemsElementName = new Rdl.ItemsChoiceType5[] { Rdl.ItemsChoiceType5.Value };
Rdl.ParameterType parablockname = new ParameterType();
parablockname.Name = "BlockName";
parablockname.Items = new object[] { strBlockName };
parablockname.ItemsElementName = new Rdl.ItemsChoiceType5[] { Rdl.ItemsChoiceType5.Value };
paras.Parameter = new ParameterType[] { parablockid ,parablockname};
lstsubobjects.Add(subreportgen.CreateSubReport(strreportname,strsubreport,paras,intt));
intSubReports++;
}
public void WriteXml(Stream stream)
{
XmlSerializer serializer = new XmlSerializer(typeof(Rdl.Report));
serializer.Serialize(stream, _report);
}
}
}
我们知道主报告在使用子报告时,都是在主报告当前目录里找。但是由于主报告是动态创建的,所以不存在主报告当前的路径,
必须通过LoadSubreportDefinition,预加载子报告。
this.reportviewer1.LocalReport.LoadSubreportDefinition(strsourcename, reportsub);
reportsub.Close();
如何使用? 看下面
{
List<BLL.AnswerBlockInfo> answerblocks = AnswerBlockController.Current.GetByWhereClause("AnswerID='" + AnswerID.ToString() + "'", "");
for (int i = 0; i < answerblocks.Count; i++)
{
BlockInfo blockinfo = BlockController.Current.Get(answerblocks[i].BlockID);
//rdlGenerator.CreateTextBox();
rdlGenerator.CreateSubreport("subreport"+blockinfo.BlockName, blockinfo.ReportFile, blockinfo.BlockID.ToString(), blockinfo.BlockName);
}
}
this.reportviewer1.Reset();
this.reportviewer1.LocalReport.LoadReportDefinition(m_rdl);
LoadSubReportDefinition();
this.reportviewer1.LocalReport.DataSources.Add(new ReportDataSource("UserField", m_dataSet.Tables[0]));
}
这样就解决了子报告的问题;
问题二。 主报告的内容都要用编码去设定吗? 会很繁琐吧?
通过http://www.gotreportviewer.com/上面的范例,想必你也看过了,动态创建报告的主要问题就是代码太繁重,每个部分都要编码输出,内容,位置,都要考虑,有没有别的办法呢。
看方法
/// <summary>
/// 加载报告,
/// </summary>
/// <param name="strreportfile">报告文件名</param>
/// <param name="intendtop">报告总高度(这个值要有,你要知道你的报告有多高,那么下面的你动态创建的控件要在他下面</param>
/// <returns></returns>
public Rdl.Report LoadReport(string strreportfile,double intendtop)
{
XmlSerializer serializer = new XmlSerializer(typeof(Rdl.Report));
Stream reader = new FileStream(strreportfile, FileMode.Open);
_report = (Rdl.Report)serializer.Deserialize(reader);
reader.Close();
intEndTop = intendtop;
return _report;
}
#endregion
我们可以把固定的内容提前设置好一个报告文件,保存起来, 通过代码把他反序列化为类,然后执行你的操作后,再保存起来.
问题三。 我是有多个字报告,但是子报告的数据源可以也是不确定的, 我也想配置,有办法吗?
你应该也知道子报告的数据源必须通过
事件内传递,所以,要传递那些数据源,我们无法提前知道, 但是也是有办法的。
笔者推荐你用.xml描述好并与子报告同名,放置在子报告同一目录下
{
// string strConnection = "server=WB_XXZX_01\\LERGER;database=Lerger108;uid=sa;pwd=disney; ";
string strblockid = e.Parameters[0].Values[0];
Guid blockid = new Guid(strblockid);
string strblockname=e.Parameters[1].Values[0];
string strConnection = System.Configuration.ConfigurationManager.ConnectionStrings["SiteSqlServer"].ConnectionString;
SqlConnection objConnection = new SqlConnection(strConnection);
XmlDocument xmlDoc = new XmlDocument();
if (System.IO.File.Exists(Server.MapPath("~/CP/Report/" + e.ReportPath + ".xml")) == true)
{
xmlDoc.Load(Server.MapPath("~/CP/Report/" + e.ReportPath + ".xml"));
XmlNode xns = xmlDoc.SelectSingleNode("report");
XmlNode xn = xns.SelectSingleNode("ReportSources");
XmlNodeList xnl = xn.ChildNodes;
foreach (XmlNode xnf in xnl)
{
XmlElement xe = (XmlElement)xnf;
string strsourcename = xe.GetAttribute("name");//显示属性值
string strsourcevalue = xe.GetAttribute("value");//显示属性值
SqlDataReader sqldatareader2 = SqlHelper.ExecuteReader(strConnection, strsourcename, AnswerID, blockid);
ReportDataSource rptDataSource2 = new ReportDataSource(strsourcevalue, sqldatareader2);
e.DataSources.Add(rptDataSource2);
}
}
}
参数AnswerID, blockid?? 是这样的,由于笔者的项目参数基本可以锁定了,只是数据源也许不同, 你可以根据自己情况,再把参数配置进去xml
<report>
<ReportSources>
<DBSource name="CP_ExportResult" value="ChartDetail">
</DBSource>
<DBSource name="CP_ExportClassResult" value="ExportClassResult">
</DBSource>
<DBSource name="CP_ExportOptionVotes" value="exportoptionvotes">
</DBSource>
</ReportSources>
<SubReports>
<SubReport name="IT部EXCEL培训调查表Sub1">
</SubReport>
</SubReports>
</report>
这样就可以了。
问题四。 如果我的子报告中也含有子报告该怎么办?
同样在xml中描述
if (System.IO.File.Exists(Server.MapPath("~/CP/Report/" + blockinfo.ReportFile + ".xml")) == true)
{
xmlDoc.Load(Server.MapPath("~/CP/Report/" + blockinfo.ReportFile + ".xml"));
XmlNode xns = xmlDoc.SelectSingleNode("report");
XmlNode xn = xns.SelectSingleNode("SubReports");
if (xn != null)
{
XmlNodeList xnl = xn.ChildNodes;
foreach (XmlNode xnf in xnl)
{
XmlElement xe = (XmlElement)xnf;
string strsourcename = xe.GetAttribute("name");//显示属性值
StreamReader reportsub = File.OpenText(@Server.MapPath("~/CP/Report/" + strsourcename + ".rdlc"));
this.reportviewer1.LocalReport.LoadSubreportDefinition(strsourcename, reportsub);
reportsub.Close();
}
}
}
<report>
<ReportSources>
<DBSource name="CP_ExportResult" value="ChartDetail">
</DBSource>
<DBSource name="CP_ExportClassResult" value="ExportClassResult">
</DBSource>
<DBSource name="CP_ExportOptionVotes" value="exportoptionvotes">
</DBSource>
</ReportSources>
<SubReports>
<SubReport name="IT部EXCEL培训调查表Sub1">
</SubReport>
</SubReports>
</report>
先到这里,20号继续整理, 父亲节快乐各位