使用ReportViewer生成动态报告--项目应用心得(一)

转自:http://www.cnblogs.com/DNNCenter/archive/2011/06/19/2084719.html

使用ReportViewer生成动态报告--项目应用心得(一)_第1张图片 

 

使用ReportViewer生成动态报告--项目应用心得(一)_第2张图片

看图,这是一个人才测评报告,报告中包含多个子部分,部分的个数,内容都是变化的。

所以子报告部分我们采用子报表来实现。

下面讲解一下构建一个这样的报告会遇到的关键问题,并且提供方案方法。

问题一。 如果报告中子报告的数量和报告源都是不确定的,如何呈现?

按照我们一般的思路,就考虑建立一个Tablix表格,绑定一组数据源,然后在里面放置子报告。









不过很遗憾,这样并不能实现, 子报告的报告源必须是定值,不能传递参数或者绑定数据源。。 这下麻烦了,那如果做了?

首先呢,参考这个
地址

http://www.gotreportviewer.com/里面有 Generate RDLC dynamically - Table 和 Generate RDLC dynamically - Matrix




项目里面,ReportDefinition.cs 文件给我们开拓了思路


通过地址 http://schemas.microsoft.com/sqlserver/

可以找到


Report Definition Language (RDL) 2005 适用于Visual Studio 2005
Report Definition Language (RDL) 2008 适用于Visual Studio 2008 /Visual Studio 2010



然后下载此文件, 在
Visual Studio 2008 命令提示符 执行
xsd /c /n:SampleRDLSchema ReportDefinition.xsd

就可以得到ReportDefinition的实体类了。 方便你动态的创建报告文件。











通过这两个范例, 就有思路了, 我们可以把主报表动态创建出来, 把子报表以代码的方式动态的追加写入主报告文件


创建子报表类


using System;
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;
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,预加载子报告。




StreamReader reportsub = File.OpenText(@Server.MapPath("~/CP/Report/" + strsourcename + ".rdlc"));
this.reportviewer1.LocalReport.LoadSubreportDefinition(strsourcename, reportsub);
reportsub.Close();




如何使用? 看下面
private void LoadSubReports()
{
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);

}
}




private void ShowReport()
{
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/上面的范例,想必你也看过了,动态创建报告的主要问题就是代码太繁重,每个部分都要编码输出,内容,位置,都要考虑,有没有别的办法呢。

看方法


#region 加载报表
/// <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

我们可以把固定的内容提前设置好一个报告文件,保存起来, 通过代码把他反序列化为类,然后执行你的操作后,再保存起来.
rdlGenerator.LoadReport(@Server.MapPath("~/CP/Report/Header/人才测评.rdlc"), 33);



问题三。 我是有多个字报告,但是子报告的数据源可以也是不确定的, 我也想配置,有办法吗?

你应该也知道子报告的数据源必须通过


LocalReport.SubreportProcessing +=new SubreportProcessingEventHandler(SubreportProcessingEventHandler);

事件内传递,所以,要传递那些数据源,我们无法提前知道, 但是也是有办法的。

笔者推荐你用.xml描述好并与子报告同名,放置在子报告同一目录下


void SubreportProcessingEventHandler(object sender, SubreportProcessingEventArgs e)
{
// 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


<?xml version="1.0" encoding="gb2312"?>
<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中描述
XmlDocument xmlDoc = new XmlDocument();
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();
}
}

}


<?xml version="1.0" encoding="gb2312"?>
<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>

 

你可能感兴趣的:(使用ReportViewer生成动态报告--项目应用心得(一))