說明:
最近項目中要用到水晶報表,水晶報表的功能的確非常強大,但是有一個問題一直困擾著我,那就是它對于每種數(shù)據(jù)表結(jié)構(gòu)都必須建一個報表文件。盡管它有兩種使用方式,“pull”模式和“push”模式。但是推模式下仍然需要先根據(jù)dataset的結(jié)構(gòu)生成相應的報表文件。這樣就很不靈活,試想如果之前并不知道數(shù)據(jù)集dataset的結(jié)構(gòu),怎么用報表?即便知道有哪些表格,如果表格數(shù)太多,對于每個表格生成一個報表文件,那也是很繁瑣的一件事。所以,有沒有方法建立一個通用報表文件,然后可以在運行時根據(jù)dataset的具體結(jié)構(gòu)來確定報表的結(jié)構(gòu)呢?查了很多資料,很少有詳細的解決方案。
在翻閱了一些資料,借鑒了多種做法后,終于解決了這個問題,下面把思路闡述如下:
第一步:
新建一個web網(wǎng)站,然后在項目中新建一個空白報表,命名為CommonReport.rpt,打開報表,在字段資源管理器中右鍵選“數(shù)據(jù)庫字段”,選擇“數(shù)據(jù)庫專家”,在彈出窗口中選“創(chuàng)建新連接”中的OLEDB,然后選擇SQL Server的提供程序,點擊下一步,如圖p1所示,跟著向?qū)ё鱿氯ィ虚g要輸入連接信息,并選擇一個數(shù)據(jù)庫,“完成”后,可以看到連上了你所選的數(shù)據(jù)庫,我選了本地SQLServer服務器上的“Northwind”數(shù)據(jù)庫,它是Sql server中自帶的一個數(shù)據(jù)庫。然后隨便選擇這個庫里的一張表,這里我選擇第一張表“Categories”添加到右面,如圖p2所示。完成后,應該是圖p3所示結(jié)果,可以看到字段資源管理器中的數(shù)據(jù)庫字段里有了一張表Categories,和它的四個字段,此時第一步完成了,后面不需要再管它了。
第二步:
在“字段資源管理器”中建立公式字段,公式字段的個數(shù)根據(jù)數(shù)據(jù)庫里所有表格中字段最多的那個表格的字段數(shù)來定。這里我建立了6個公式字段,注意,這些公式字段都是空的。然后將這6個公式字段依次拖放到報表的詳細資料欄里。
右鍵點擊某個公式字段對應的頁眉中文本對象(如圖4中的“FormulaField6”)彈出菜單,選擇“設置對象格式”,得到圖中的格式化編輯器對話框。選擇對話框中的“抑制顯示”復選框后面的圖標按鈕,然后在彈出的對話框中輸入圖p4右下方的一段代碼(if… else…),如圖p4所示。
完成后點“保存并關閉”。剩下的其它幾個字段如法炮制。完成后就可以保存報表文件,報表制作完成。
第三步:
新建網(wǎng)頁用來測試我們生成的報表,首先生成一個GetDataSet.aspx頁面,該頁面裝載時連接數(shù)據(jù)庫,生成DataSet,并將它存入Session對象中。還有一個按鈕,在事件處理代碼中調(diào)用另外一個頁面showReport.aspx。
showReport.aspx這個頁面里面有個報表瀏覽器CrystalReportViewer控件,它從session中取出DataSet,用CrystalReportViewer顯示DataSet中的數(shù)據(jù)。
GetDataSet.aspx.cs的代碼:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;
public partial class GetDataSet : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection sqlConnection = new SqlConnection ( "Server=localhost;User Id=sa;Initial Catalog=Northwind" ) ;
sqlConnection.Open();
SqlDataAdapter sqlDataAdapter1 = new SqlDataAdapter("SELECT CustomerID , CompanyName,City,Country FROM Customers", sqlConnection);
DataSet dsDataSet = new DataSet () ;
sqlDataAdapter1.Fill (dsDataSet,"Customers") ;
//使用SqlDataAdapter的Fill方法填充DataSet
sqlConnection.Close ( ) ;
//關閉數(shù)據(jù)連接
Session["DataSet"] = dsDataSet;
}
protected void Button1_Click(object sender, EventArgs e)
{
Page.RegisterClientScriptBlock("open", "<script language='javascript'>window.open('showReport.aspx','_blank')</script>");
}
}
showReport.aspx.cs的詳細代碼:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using CrystalDecisions.CrystalReports.Engine;
public partial class showReport : System.Web.UI.Page
{
private ReportDocument CommonReport;
///<summary>
///根據(jù)具體查詢得到的ADO.NET數(shù)據(jù)集配置水晶報表
///</summary>
private void ConfigureCrystalReports()
{
//生成一個報表對象
CommonReport = new ReportDocument();
//報表文件路徑
string reportPath = Server.MapPath("CommonReport.rpt");
//根據(jù)路徑裝載指定報表文件,以進行報表配置操作
CommonReport.Load(reportPath);
//從Session中取得數(shù)據(jù)集,該數(shù)據(jù)集是從其它頁面查詢數(shù)據(jù)庫得到后保存在Session中的
DataSet dataSet = (DataSet)Session["DataSet"];
//將數(shù)據(jù)集中的表格的名稱修改為報表文件中連接的數(shù)據(jù)表的名字,保證兩個表格名稱一致
dataSet.Tables[0].TableName=CommonReport.Database.Tables[0].Name;
//獲取數(shù)據(jù)集中字段個數(shù),也是報表中要顯示的列的個數(shù)
int CountOfColumns = dataSet.Tables[0].Columns.Count;
//定義一個字符串數(shù)組,在for循環(huán)中生成每個公式字段對象的文本(格式是"{tablename.columnname}"),并保存到數(shù)組中以備使用
String[] Text4formularFields = new String[CountOfColumns];
for (int index = 0; index < CountOfColumns; index++)
{
Text4formularFields[index] = "{" + CommonReport.Database.Tables[0].Name + "." + dataSet.Tables[0].Columns[index].ColumnName + "}";
}
//獲取報表文件的數(shù)據(jù)定義對象
DataDefinition dataDefinition = CommonReport.DataDefinition;
//獲取數(shù)據(jù)對象中的公式字段集合
FormulaFieldDefinitions formularFields = dataDefinition.FormulaFields;
for (int columnIndex = 0; columnIndex < CountOfColumns;columnIndex++)
{
//將公式字段的空白文本替換為根據(jù)當前數(shù)據(jù)集dataSet具體字段生成的文本
formularFields[columnIndex].Text=Text4formularFields[columnIndex];
//頁眉中的文本對象(也就是報表中每列的標題)設置為dataSet的具體字段名
TextObject CurrentText = (TextObject)(CommonReport.ReportDefinition.Sections["Section2"].ReportObjects[columnIndex]);
CurrentText.Text = dataSet.Tables[0].Columns[columnIndex].ColumnName;
}
//設置報表對象的數(shù)據(jù)源為dataSet中的同名表格
CommonReport.SetDataSource(dataSet.Tables[CommonReport.Database.Tables[0].Name]);
//綁定到報表瀏覽器
CrystalReportViewer1.ReportSource = CommonReport;
}
private void Page_Init(object sender, EventArgs e)
{
ConfigureCrystalReports();
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
上面兩個頁面運行后的效果,如圖p5所示。
如果把GetDataSet.aspx.cs文件中的SQL查詢字符串換成”SELECT EmployeeID , LastName,FirstName,Title,Country FROM Employees”,其它的地方都不改動,報表文件還是用CommonReport.rpt。得到的報表輸出如圖p6所示。
現(xiàn)在我們可以看到,不管數(shù)據(jù)庫服務器上有多少數(shù)據(jù)表,我們只要用CommonReport.rpt這一個報表文件就可以顯示了。
注意:
1, 做第一步的原因只是因為需要一個表的連接,否則在調(diào)用report對象的setDataSource()方法時會報錯,提示“報表中沒有表存在”。換句話說就是為報表文件生成表格時提供一個表名,報表最終顯示的并不是來自這張表里的數(shù)據(jù),
2, 圖4中那段代碼是用Crystal的語言寫的,作用是判斷當前列是否有數(shù)據(jù),如果沒有則不在頁眉中顯示該列的標題。它的作用就是因為我們所設的公式字段并不一定都用得上,因為我們是按字段最多的情況確定的公式字段個數(shù),多出來的公式字段就要屏蔽掉。
3, showReport.aspx.cs中CommonReport.SetDataSource(dataSet.Tables[CommonReport.Database.Tables[0].Name]);這個語句,參數(shù)必須明確指定dataSet中的表名,如果只用dataSet作為參數(shù),則報表中不顯示數(shù)據(jù),不知道是什么原因。
4, 這里是用公式字段解決的,也可以用未綁定字段來解決這個問題,道理是一樣的。
5, 如圖p6所示,第一列沒有對齊。這是后來發(fā)現(xiàn)的問題,沒找到什么原因。
參考資料:
http://blog.csdn.net/haibodotnet/archive/2003/11/09/21504.aspx,csdn上的一篇文章,參考了他的思路
http://msdn2.microsoft.com/zh-cn/library/ms225984(VS.80).aspx,msdn上水晶報表的類庫