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

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

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

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

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

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

 

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

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

 

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

首先呢,参考这个

地址

 

        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>

 

 

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

 

 


 

你可能感兴趣的:(view)