使用ReportViewer產生動態報告–項目應用心得(一)

來源:互聯網
上載者:User

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

 

 

看圖,這是一個人才測評報告,報告中包含多個子部分,部分的個數,內容都是變化的。

所以子報告部分我們採用子報表來實現。

下面講解一下構建一個這樣的報告會遇到的關鍵問題,並且提供方案方法。

問題一。 如果報告中子報告的數量和報告源都是不確定的,如何呈現?

按照我們一般的思路,就考慮建立一個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>

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.