是时候改造我原来的报表系统了,几天的工作总结下。
在VS2005里面的Report功能好强,把我原来做得很多工作都枪毙了,痛并快乐的感觉。
对现在的项目,要使用MS Repor有2个最烦人问题:
1、没有提供RDLC的图形设计界面的运行时控件,在网上找了一下,也没有发现谁做好了;
2、强结构作为数据源。
虽然客户的报表模板都是我做,但是我还是不愿意在设计环境下来设计报表。要是有个RDLC的图形设计界面控件多好,理论上作一个实现一些基本功能,至少满足现在这个项目需要的,肯定没多大问题,但是需要时间,慢慢再说吧。
好了,我暂时开VS设计报表,可是在VS的IDE里,设计报表时需要强结构的数据源,我做不到啊。这个项目的报表又多又乱,几乎天天有新报表,天天在改久报表。这个现象也怪不了谁,公司在急速发展,新业务和新作业方式不断地加入到系统中,各处对报表的需求量肯定会不断增加;还有一点,我没法对业务完全了解,自己出来的报表都不知道是不是客户想要得。所以,我不能把报表数据源的结构定下来,一定要方便我改动。定下一点总需求,改造后的Report系统保持添加修改任何报表不需要改代码的特性。
对于简单报表现在有了一个思路。step by setp
1、只返回一个Table作为数据源,在这个单表里面包含主-子-子----子的所有数据。其实我习惯称呼其为分层,有人叫分组。拿到要做的报表,就在SQL Server里建一个相应的SP,,返回值就一个Table就得了。
2、做一个DefaultReportTemplate.rdlc, 全空;定义一个DataSet,里面一个Table, 里面一个Field。把这个DataSet添加到DefaultReportTemplate.rdlc。
3、做一个asmx或者aspx,输入参数为SP_Name,ReportTemplateName。功能很简单,用XML打开DefaultReportTemplate.rdlc,查询到该SP得Fileds,替换XML里面数据表的字段定义部分, Saveas -->ReportTemplateName。再提供一个下载到本地的功能也行,自己从服务上下载也行。
4、开个VS2005空项目,打开下载的rdlc. 很好,虽然没有图形化的数据源显示出来,用不上托拽字段的功能,但是在所有的属性页都能使用我替换上去的Field。
5、设计好rdlc,上传到系统report template空间。
DefaultReportTemplate.rdlc
<?
xml version="1.0" encoding="utf-8"
?>
<
Report
xmlns
="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition"
xmlns:rd
="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner"
>
<
DataSources
>
<
DataSource
Name
="ReportDataSource"
>
<
ConnectionProperties
>
<
ConnectString
/>
<
DataProvider
>
SQL
</
DataProvider
>
</
ConnectionProperties
>
<
rd:DataSourceID
>
a03e342a-c8ff-428e-87c7-8a5f9d36889b
</
rd:DataSourceID
>
</
DataSource
>
</
DataSources
>
<
Width
>
6.5in
</
Width
>
<
Body
>
<
Height
>
2in
</
Height
>
</
Body
>
<
rd:InitialLanguage
>
true
</
rd:InitialLanguage
>
<
rd:InitialDimensions
>
<
rd:UnitType
>
Inch
</
rd:UnitType
>
<
rd:LeftMargin
>
1in
</
rd:LeftMargin
>
<
rd:RightMargin
>
1in
</
rd:RightMargin
>
<
rd:TopMargin
>
1in
</
rd:TopMargin
>
<
rd:BottomMargin
>
1in
</
rd:BottomMargin
>
<
rd:PageWidth
>
8.5in
</
rd:PageWidth
>
<
rd:PageHeight
>
11in
</
rd:PageHeight
>
<
rd:ColumnSpacing
>
0.5in
</
rd:ColumnSpacing
>
</
rd:InitialDimensions
>
<
rd:InitialDimensions
>
<
rd:UnitType
>
Cm
</
rd:UnitType
>
<
rd:Width
>
16cm
</
rd:Width
>
<
rd:Height
>
5cm
</
rd:Height
>
<
rd:LeftMargin
>
2.5cm
</
rd:LeftMargin
>
<
rd:RightMargin
>
2.5cm
</
rd:RightMargin
>
<
rd:TopMargin
>
2.5cm
</
rd:TopMargin
>
<
rd:BottomMargin
>
2.5cm
</
rd:BottomMargin
>
<
rd:GridSpacing
>
0.25cm
</
rd:GridSpacing
>
<
rd:PageWidth
>
21cm
</
rd:PageWidth
>
<
rd:PageHeight
>
29.7cm
</
rd:PageHeight
>
<
rd:ColumnSpacing
>
1cm
</
rd:ColumnSpacing
>
</
rd:InitialDimensions
>
<
DataSets
>
<
DataSet
Name
="DataSetReport"
>
<
rd:DataSetInfo
>
<
rd:DataSetName
>
DataSetReport
</
rd:DataSetName
>
<
rd:TableName
>
DataTableReport
</
rd:TableName
>
</
rd:DataSetInfo
>
<
Query
>
<
rd:UseGenericDesigner
>
true
</
rd:UseGenericDesigner
>
<
CommandText
/>
<
DataSourceName
>
ReportDataSource
</
DataSourceName
>
</
Query
>
<
Fields
>
<
Field
Name
="Filed1"
>
<
rd:TypeName
>
String
</
rd:TypeName
>
<
DataField
>
Filed1
</
DataField
>
</
Field
>
</
Fields
>
</
DataSet
>
</
DataSets
>
</
Report
>
根据SP的返回Table替换DefaultReportTemplate.rdlc的字段定义区
public
void
CreateRDLC(
string
sp,
string
filename)

{
try

{
DataSet ds = GetSPStructure(sp);
XmlDocument sourceDoc = new XmlDocument();
XPathNavigator navigator;
XmlNamespaceManager manager;
string tempdir =AppDomain.CurrentDomain.BaseDirectory;
string tempfilepath =Path.Combine(tempdir,@"Templates/ReportDefaulte.rdlc");

string objfilepath =Path.Combine(tempdir,@"Templates/"+filename);

sourceDoc.Load(tempfilepath);

navigator = sourceDoc.CreateNavigator();
manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("a", "http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition");
manager.AddNamespace("rd", "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner");

XmlNode xnFieldTemp = sourceDoc.SelectSingleNode("/a:Report/a:DataSets/a:DataSet/a:Fields/a:Field",manager);
XmlNode xnDataSet = xnFieldTemp.ParentNode;

int fieldCount = ds.Tables["ReportFields"].Columns.Count;
xnFieldTemp.Attributes["Name"].Value = ds.Tables["DataTableReport"].Columns[0].ColumnName;
xnFieldTemp["DataField"].InnerText = ds.Tables["DataTableReport"].Columns[0].ColumnName;
xnFieldTemp["rd:TypeName"].InnerText = ds.Tables["DataTableReport"].Columns[0].DataType.ToString();

for (int i =1 ; i<fieldCount;i++)

{
XmlNode xnNewField = xnFieldTemp.Clone();
xnNewField.Attributes["Name"].Value = ds.Tables["DataTableReport"].Columns[i].ColumnName;
xnNewField["DataField"].InnerText = ds.Tables["DataTableReport"].Columns[i].ColumnName;
xnNewField["rd:TypeName"].InnerText = ds.Tables["DataTableReport"].Columns[i].DataType.ToString();
xnDataSet.AppendChild(xnNewField);
}
sourceDoc.Save(objfilepath);
}
catch (Exception e)

{
System.Console.WriteLine(e.ToString());
throw;
}

}

private
DataSet GetSPStructure(
string
sp)

{
DataSet tempData = new DataSet();
System.Data.SqlClient.SqlCommand sqlSelectCommand1 = new System.Data.SqlClient.SqlCommand();
System.Data.SqlClient.SqlDataAdapter sqlQuery = new System.Data.SqlClient.SqlDataAdapter() ;
sqlSelectCommand1.CommandText = "[sp_sproc_columns]";
sqlSelectCommand1.CommandType = System.Data.CommandType.StoredProcedure;
sqlSelectCommand1.Connection = this.conn;
sqlSelectCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter("@RETURN_VALUE", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.ReturnValue, false, ((System.Byte)(0)), ((System.Byte)(0)), "", System.Data.DataRowVersion.Current, null));
sqlSelectCommand1.Parameters.Add(new System.Data.SqlClient.SqlParameter("@procedure_name", System.Data.SqlDbType.NVarChar, 390));
sqlQuery.SelectCommand = sqlSelectCommand1;
sqlSelectCommand1.Parameters["@procedure_name"].Value = sp;
sqlQuery.Fill(tempData,"Parameters");

string sql="exec " + sp + " @EditionIDs='-2'";
for (int i=1;i<tempData.Tables[0].Rows.Count-1;i++)

{
string Type_Name = tempData.Tables[0].Rows[i]["TYPE_NAME"].ToString();
string Para_Name = tempData.Tables[0].Rows[i]["COLUMN_NAME"].ToString();
if (Type_Name=="int")
sql=sql + "," + Para_Name + "=0";
if (Type_Name=="nvarchar" || Type_Name=="varchar" || Type_Name=="nchar" || Type_Name=="char")
sql=sql + "," + Para_Name + "=''";
if (Type_Name=="datetime")
sql=sql + "," + Para_Name + "='1900-01-01'";
}
sqlQuery.SelectCommand.CommandType=System.Data.CommandType.Text;
sqlQuery.SelectCommand.CommandText = sql;
sqlQuery.Fill(tempData,"ReportFields");
return tempData;
}
对原系统的改造应该不会有多大,Reprot Template Main Window 提供一个选择rdlc文件的combobox。在ReportOutpot window,砍掉以前的代码,放个ReportViewer上去,加上以下代码:这了copy了一点别人的代码,望事主见谅。
private
void
ViewReport(DataSet dsReport,
string
templatfullpath)

{

XmlDocument sourceDoc = new XmlDocument();
sourceDoc.Load(templatfullpath);
MemoryStream ms = new MemoryStream();
XmlSerializer serializer = new XmlSerializer(typeof(XmlDocument));
serializer.Serialize(ms, sourceDoc);
ms.Position = 0;

this.reportViewer1.LocalReport.LoadReportDefinition(ms);
this.reportViewer1.LocalReport.DataSources.Add(new ReportDataSource("DataSetReport", dsReport.Tables[0]));
this.reportViewer1.LocalReport.Refresh();
this.reportViewer1.RefreshReport();
}
直接打印和自动导出到Excel和PDF的部分还没去研究,应该不会出什么篓子吧。