本文转自:http://www.cnblogs.com/pszw/archive/2012/07/19/2599937.html
前言
最近接到一个需求:在给定的数据源中,某(些)列,可能需要单独统计,是否单独统计需要根据报表配置来决定。由于项目中一直使用RDLC来生成报表,临时为了一个需求换一种技术也不是很现实,所以自己捉摸了下。
认识RDLC
RDLC的主要有三个部分:
(1)*.rdlc文件,本质是一个XML文件,这里定义了报表样式;
(2)*.xsd文件,也是一个XML文件,这里定义了数据源格式;
(3)*.aspx文件,呈现报表的web页面。
注:RDLC是什么,可参考蜡人张的博客:http://www.cnblogs.com/waxdoll/archive/2006/02/25/337713.html
如何实现动态
(1)LocalReport对象提供了方法LoadReportDefinition(Stream stream)和属性ReportPath保证了我们不仅可以从流中读取文件,也可以指定本地文件路径加载rdlc文件;
(2).rdlc,.xsd都是xml文件,可使用XmlDocument进行读写操作。
实例
下面实现一个学生成绩统计报表为例,介绍如何实现动态列。
第一步 准备工作
新建空web项目->添加xsd文件,创建一个table(文件名Students.xsd)->添加rdlc文件,与xsd的table关联,并绑定相关字段(文件名FirstRdlc.rdlc)。
Students.xsd设计器视图:
对应的xml文件:
观察这段xml,会注意这段代码:
<xs:element name="Class" msprop:Generator_ColumnVarNameInTable="columnClass" msprop:Generator_ColumnPropNameInRow="Class" msprop:Generator_ColumnPropNameInTable="ClassColumn" msprop:Generator_UserColumnName="Class" type="xs:string" minOccurs="0" />
这句代码定义了报表数据源的“Class”这列。可想而知,我们如果动态添加一列,这里势必应该要修改。
FirstRdlc.rdlc文件设计器视图:
对应的xml文件如下:
仔细观察这段xml文件,不难看出有几部分代码是值得关注的:
(1)路径Report/DataSets/DataSet/Fields下得Field节点,这里定义的是同数据源相关的列;
(2)路径Report/DataSets/DataSet/rd:DataSetInfo节点,这里定义了rdlc关联的xsd文件的路径;
(3)路径Report/Body/ReportItems/Tablix/TablixBody/TablixColumns下的TablixColumn节点,这里应该定义了RDLC报表的列数
(4)路径Report/Body/ReportItems/Tablix/TablixBody/TablixRows下的TablixRow。从名称可知是报表行相关内容,其中每个TablixRow,又定义了单元格信息(在TablixCells下的TablixCell节点)。这里默认情况下有两行,第一行定义了报表列头显示内容,如:姓名,性别等,第二行定义了报表数据的绑定项。如:姓名绑定到xsd的Name字段。这里便有:<Value>=Fields!Name.Value</Value>
所以,这以上几处在我们修改xml时,势必可能需要修改。其实,这里还有一处需要修改,路径:Report/Body/ReportItems/Tablix/TablixColumnHierarchy/TablixMembers下的TablixMember,该节点个数一定要和报表列数相同。否则编译便会报错。
第二步:操作XML,动态添加列(GPA列)
(1)操作XML文件使用XmlDocument类,需要添加节点时,可以使用XmlNode.CloneNode(bool)方法。
(2)操作xsd文件,将需要添加的列,加入到xsd中,并保存指定路径(保存文件命名为Student1.xsd);
(2)修改rdlc文件,包括,待添加列、xsd文件路径等,中(或保存为rdlc文件);
1 /// <summary> 2 /// 修改RDLC文件 3 /// </summary> 4 /// <returns></returns> 5 private XmlDocument ModifyRdlc() 6 { 7 XmlDocument xmlDoc = new XmlDocument(); 8 9 xmlDoc.Load(AppDomain.CurrentDomain.BaseDirectory + "FirstRdlc.rdlc"); 10 11 //添加Field节点 12 XmlNodeList fileds = xmlDoc.GetElementsByTagName("Fields"); 13 14 XmlNode filedNode = fileds.Item(0).FirstChild.CloneNode(true); 15 filedNode.Attributes["Name"].Value = "GPA"; 16 filedNode.FirstChild.InnerText = "GPA"; 17 fileds.Item(0).AppendChild(filedNode); 18 19 //添加TablixColumn 20 21 XmlNodeList tablixColumns = xmlDoc.GetElementsByTagName("TablixColumns"); 22 XmlNode tablixColumn = tablixColumns.Item(0).FirstChild; 23 XmlNode newtablixColumn = tablixColumn.CloneNode(true); 24 tablixColumns.Item(0).AppendChild(newtablixColumn); 25 26 //TablixMember 27 XmlNodeList tablixMembers = xmlDoc.GetElementsByTagName("TablixColumnHierarchy"); 28 29 XmlNode tablixMember = tablixMembers.Item(0).FirstChild.FirstChild; 30 XmlNode newTablixMember = tablixMember.CloneNode(true); 31 tablixMembers.Item(0).FirstChild.AppendChild(newTablixMember); 32 33 XmlNodeList tablixRows = xmlDoc.GetElementsByTagName("TablixRows"); 34 35 //TablixRows1 36 var tablixRowsRowCells1 = tablixRows.Item(0).FirstChild.ChildNodes[1]; 37 XmlNode tablixRowCell1 = tablixRowsRowCells1.FirstChild; 38 XmlNode newtablixRowCell1 = tablixRowCell1.CloneNode(true); 39 var textBox1 = newtablixRowCell1.FirstChild.ChildNodes[0]; 40 textBox1.Attributes["Name"].Value = "GPA1"; 41 42 var paragraphs = textBox1.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "Paragraphs").FirstOrDefault(); 43 paragraphs.FirstChild.FirstChild.FirstChild.FirstChild.InnerText = "GPA"; 44 var defaultName1 = textBox1.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "rd:DefaultName").FirstOrDefault().InnerText = "GPA1"; 45 46 tablixRowsRowCells1.AppendChild(newtablixRowCell1); 47 48 //TablixRows2 49 var tablixRowsRowCells2 = tablixRows.Item(0).ChildNodes[1].ChildNodes[1]; 50 XmlNode tablixRowCell2 = tablixRowsRowCells2.FirstChild; 51 XmlNode newtablixRowCell2 = tablixRowCell2.CloneNode(true); 52 var textBox2 = newtablixRowCell2.FirstChild.ChildNodes[0]; 53 textBox2.Attributes["Name"].Value = "GPA"; 54 55 var paragraphs2 = textBox2.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "Paragraphs").FirstOrDefault(); 56 paragraphs2.FirstChild.FirstChild.FirstChild.FirstChild.InnerText = "=Fields!GPA.Value"; 57 var defaultName2 = textBox2.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "rd:DefaultName").FirstOrDefault().InnerText = "GPA"; 58 59 tablixRowsRowCells2.AppendChild(newtablixRowCell2); 60 61 xmlDoc.Save(AppDomain.CurrentDomain.BaseDirectory + "FirstRdlc1.rdlc"); 62 return xmlDoc; 63 }
(3)将得到的XmlDocument实例,序列化到MemoryStream。
1 /// <summary> 2 /// 序列化到内存流 3 /// </summary> 4 /// <returns></returns> 5 private Stream GetRdlcStream(XmlDocument xmlDoc) 6 { 7 Stream ms = new MemoryStream(); 8 XmlSerializer serializer = new XmlSerializer(typeof(XmlDocument)); 9 serializer.Serialize(ms, xmlDoc); 10 11 ms.Position = 0; 12 return ms; 13 }
第三步:加载报表,并显示
(1)添加一个Page页面,并添加ReportView控件和ScriptManager控件,页面代码如下:
(2)加载报表定义,并绑定数据源(使用LoadReportDefinition(Stream stream)方法加载MemoryStream中信息)
1 /// <summary> 2 /// 加载报表 3 /// </summary> 4 private void LoadReport() 5 { 6 //获取数据源 7 DataTable dataSource = GetDataSource(); 8 9 //修改xsd文件 10 ModifyXSD(); 11 12 //修改rdlc文件 13 XmlDocument xmlDoc = ModifyRdlc(); 14 15 //将修改后的rdlc文档序列化到内存流中 16 Stream stream = GetRdlcStream(xmlDoc); 17 18 //加载报表定义 19 rvDemo.LocalReport.LoadReportDefinition(stream); 20 //rvDemo.LocalReport.ReportPath = "FirstRdlc.rdlc"; 21 22 //添加数据源,rvDemo是页面上的ReportView控件 23 rvDemo.LocalReport.DataSources.Add(new ReportDataSource("dsStudent", dt)); 24 rvDemo.LocalReport.Refresh(); 25 } 26 27 /// <summary> 28 /// 获取数据源 29 /// </summary> 30 /// <returns></returns> 31 private DataTable GetDataSource() 32 { 33 //伪造一个数据源 34 DataTable dt = new DataTable(); 35 dt.Columns.AddRange(new DataColumn[] 36 { 37 new DataColumn() { ColumnName = "RecId" }, 38 new DataColumn() { ColumnName = "Name" }, 39 new DataColumn() { ColumnName = "Age" }, 40 new DataColumn() { ColumnName = "Class" }, 41 new DataColumn() { ColumnName = "Scores" }, 42 new DataColumn() { ColumnName = "GPA" } 43 }); 44 45 DataRow dr = dt.NewRow(); 46 dr["RecId"] = "1"; 47 dr["Name"] = "小明"; 48 dr["Age"] = "26"; 49 dr["Class"] = "1年级"; 50 dr["Scores"] = "90"; 51 dr["GPA"] = "4.0"; 52 53 dt.Rows.Add(dr); 54 return dt; 55 }
如此,我们便可以动态的添加GPA这列到报表上了,结果如下:
源码地址:HelloRdlc.7z
前言
最近接到一个需求:在给定的数据源中,某(些)列,可能需要单独统计,是否单独统计需要根据报表配置来决定。由于项目中一直使用RDLC来生成报表,临时为了一个需求换一种技术也不是很现实,所以自己捉摸了下。
认识RDLC
RDLC的主要有三个部分:
(1)*.rdlc文件,本质是一个XML文件,这里定义了报表样式;
(2)*.xsd文件,也是一个XML文件,这里定义了数据源格式;
(3)*.aspx文件,呈现报表的web页面。
注:RDLC是什么,可参考蜡人张的博客:http://www.cnblogs.com/waxdoll/archive/2006/02/25/337713.html
如何实现动态
(1)LocalReport对象提供了方法LoadReportDefinition(Stream stream)和属性ReportPath保证了我们不仅可以从流中读取文件,也可以指定本地文件路径加载rdlc文件;
(2).rdlc,.xsd都是xml文件,可使用XmlDocument进行读写操作。
实例
下面实现一个学生成绩统计报表为例,介绍如何实现动态列。
第一步 准备工作
新建空web项目->添加xsd文件,创建一个table(文件名Students.xsd)->添加rdlc文件,与xsd的table关联,并绑定相关字段(文件名FirstRdlc.rdlc)。
Students.xsd设计器视图:
对应的xml文件:
观察这段xml,会注意这段代码:
<xs:element name="Class" msprop:Generator_ColumnVarNameInTable="columnClass" msprop:Generator_ColumnPropNameInRow="Class" msprop:Generator_ColumnPropNameInTable="ClassColumn" msprop:Generator_UserColumnName="Class" type="xs:string" minOccurs="0" />
这句代码定义了报表数据源的“Class”这列。可想而知,我们如果动态添加一列,这里势必应该要修改。
FirstRdlc.rdlc文件设计器视图:
对应的xml文件如下:
仔细观察这段xml文件,不难看出有几部分代码是值得关注的:
(1)路径Report/DataSets/DataSet/Fields下得Field节点,这里定义的是同数据源相关的列;
(2)路径Report/DataSets/DataSet/rd:DataSetInfo节点,这里定义了rdlc关联的xsd文件的路径;
(3)路径Report/Body/ReportItems/Tablix/TablixBody/TablixColumns下的TablixColumn节点,这里应该定义了RDLC报表的列数
(4)路径Report/Body/ReportItems/Tablix/TablixBody/TablixRows下的TablixRow。从名称可知是报表行相关内容,其中每个TablixRow,又定义了单元格信息(在TablixCells下的TablixCell节点)。这里默认情况下有两行,第一行定义了报表列头显示内容,如:姓名,性别等,第二行定义了报表数据的绑定项。如:姓名绑定到xsd的Name字段。这里便有:<Value>=Fields!Name.Value</Value>
所以,这以上几处在我们修改xml时,势必可能需要修改。其实,这里还有一处需要修改,路径:Report/Body/ReportItems/Tablix/TablixColumnHierarchy/TablixMembers下的TablixMember,该节点个数一定要和报表列数相同。否则编译便会报错。
第二步:操作XML,动态添加列(GPA列)
(1)操作XML文件使用XmlDocument类,需要添加节点时,可以使用XmlNode.CloneNode(bool)方法。
(2)操作xsd文件,将需要添加的列,加入到xsd中,并保存指定路径(保存文件命名为Student1.xsd);
(2)修改rdlc文件,包括,待添加列、xsd文件路径等,中(或保存为rdlc文件);
1 /// <summary> 2 /// 修改RDLC文件 3 /// </summary> 4 /// <returns></returns> 5 private XmlDocument ModifyRdlc() 6 { 7 XmlDocument xmlDoc = new XmlDocument(); 8 9 xmlDoc.Load(AppDomain.CurrentDomain.BaseDirectory + "FirstRdlc.rdlc"); 10 11 //添加Field节点 12 XmlNodeList fileds = xmlDoc.GetElementsByTagName("Fields"); 13 14 XmlNode filedNode = fileds.Item(0).FirstChild.CloneNode(true); 15 filedNode.Attributes["Name"].Value = "GPA"; 16 filedNode.FirstChild.InnerText = "GPA"; 17 fileds.Item(0).AppendChild(filedNode); 18 19 //添加TablixColumn 20 21 XmlNodeList tablixColumns = xmlDoc.GetElementsByTagName("TablixColumns"); 22 XmlNode tablixColumn = tablixColumns.Item(0).FirstChild; 23 XmlNode newtablixColumn = tablixColumn.CloneNode(true); 24 tablixColumns.Item(0).AppendChild(newtablixColumn); 25 26 //TablixMember 27 XmlNodeList tablixMembers = xmlDoc.GetElementsByTagName("TablixColumnHierarchy"); 28 29 XmlNode tablixMember = tablixMembers.Item(0).FirstChild.FirstChild; 30 XmlNode newTablixMember = tablixMember.CloneNode(true); 31 tablixMembers.Item(0).FirstChild.AppendChild(newTablixMember); 32 33 XmlNodeList tablixRows = xmlDoc.GetElementsByTagName("TablixRows"); 34 35 //TablixRows1 36 var tablixRowsRowCells1 = tablixRows.Item(0).FirstChild.ChildNodes[1]; 37 XmlNode tablixRowCell1 = tablixRowsRowCells1.FirstChild; 38 XmlNode newtablixRowCell1 = tablixRowCell1.CloneNode(true); 39 var textBox1 = newtablixRowCell1.FirstChild.ChildNodes[0]; 40 textBox1.Attributes["Name"].Value = "GPA1"; 41 42 var paragraphs = textBox1.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "Paragraphs").FirstOrDefault(); 43 paragraphs.FirstChild.FirstChild.FirstChild.FirstChild.InnerText = "GPA"; 44 var defaultName1 = textBox1.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "rd:DefaultName").FirstOrDefault().InnerText = "GPA1"; 45 46 tablixRowsRowCells1.AppendChild(newtablixRowCell1); 47 48 //TablixRows2 49 var tablixRowsRowCells2 = tablixRows.Item(0).ChildNodes[1].ChildNodes[1]; 50 XmlNode tablixRowCell2 = tablixRowsRowCells2.FirstChild; 51 XmlNode newtablixRowCell2 = tablixRowCell2.CloneNode(true); 52 var textBox2 = newtablixRowCell2.FirstChild.ChildNodes[0]; 53 textBox2.Attributes["Name"].Value = "GPA"; 54 55 var paragraphs2 = textBox2.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "Paragraphs").FirstOrDefault(); 56 paragraphs2.FirstChild.FirstChild.FirstChild.FirstChild.InnerText = "=Fields!GPA.Value"; 57 var defaultName2 = textBox2.ChildNodes.Cast<XmlNode>().Where(item => item.Name == "rd:DefaultName").FirstOrDefault().InnerText = "GPA"; 58 59 tablixRowsRowCells2.AppendChild(newtablixRowCell2); 60 61 xmlDoc.Save(AppDomain.CurrentDomain.BaseDirectory + "FirstRdlc1.rdlc"); 62 return xmlDoc; 63 }
(3)将得到的XmlDocument实例,序列化到MemoryStream。
1 /// <summary> 2 /// 序列化到内存流 3 /// </summary> 4 /// <returns></returns> 5 private Stream GetRdlcStream(XmlDocument xmlDoc) 6 { 7 Stream ms = new MemoryStream(); 8 XmlSerializer serializer = new XmlSerializer(typeof(XmlDocument)); 9 serializer.Serialize(ms, xmlDoc); 10 11 ms.Position = 0; 12 return ms; 13 }
第三步:加载报表,并显示
(1)添加一个Page页面,并添加ReportView控件和ScriptManager控件,页面代码如下:
(2)加载报表定义,并绑定数据源(使用LoadReportDefinition(Stream stream)方法加载MemoryStream中信息)
1 /// <summary> 2 /// 加载报表 3 /// </summary> 4 private void LoadReport() 5 { 6 //获取数据源 7 DataTable dataSource = GetDataSource(); 8 9 //修改xsd文件 10 ModifyXSD(); 11 12 //修改rdlc文件 13 XmlDocument xmlDoc = ModifyRdlc(); 14 15 //将修改后的rdlc文档序列化到内存流中 16 Stream stream = GetRdlcStream(xmlDoc); 17 18 //加载报表定义 19 rvDemo.LocalReport.LoadReportDefinition(stream); 20 //rvDemo.LocalReport.ReportPath = "FirstRdlc.rdlc"; 21 22 //添加数据源,rvDemo是页面上的ReportView控件 23 rvDemo.LocalReport.DataSources.Add(new ReportDataSource("dsStudent", dt)); 24 rvDemo.LocalReport.Refresh(); 25 } 26 27 /// <summary> 28 /// 获取数据源 29 /// </summary> 30 /// <returns></returns> 31 private DataTable GetDataSource() 32 { 33 //伪造一个数据源 34 DataTable dt = new DataTable(); 35 dt.Columns.AddRange(new DataColumn[] 36 { 37 new DataColumn() { ColumnName = "RecId" }, 38 new DataColumn() { ColumnName = "Name" }, 39 new DataColumn() { ColumnName = "Age" }, 40 new DataColumn() { ColumnName = "Class" }, 41 new DataColumn() { ColumnName = "Scores" }, 42 new DataColumn() { ColumnName = "GPA" } 43 }); 44 45 DataRow dr = dt.NewRow(); 46 dr["RecId"] = "1"; 47 dr["Name"] = "小明"; 48 dr["Age"] = "26"; 49 dr["Class"] = "1年级"; 50 dr["Scores"] = "90"; 51 dr["GPA"] = "4.0"; 52 53 dt.Rows.Add(dr); 54 return dt; 55 }
如此,我们便可以动态的添加GPA这列到报表上了,结果如下:
源码地址:HelloRdlc.7z