DNNCenter

导航

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

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

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

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

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

       按照我们一般的思路,就考虑建立一个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[2as Rdl.BodyType;
            Rdl.ReportItemsType reportItems = body.Items[0as 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>

 

 

先到这里,20号继续整理, 父亲节快乐各位

 

 


 

posted on 2011-06-19 17:42  懒洋洋  阅读(3465)  评论(2编辑  收藏  举报