RDLC报表之动态生成报表
Posted CoderBaby
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RDLC报表之动态生成报表相关的知识,希望对你有一定的参考价值。
前段时间,做了RDLC报表,主要是三块功能:
1、从DataGrid提取(包括最新的增删改)数据,自动生成对应的RDLC报表文件(以流的形式驻存在内存中),用ReportViewer类来展示、打印、排版、预览、分页
提供一个提取任意控件数据的通用接口,然后拼接成DataTable这种网状的格子。DataGrid里修改、增加、删除等数据变动,立即同步更新到报表
2、给一个简单的RDLC模板,提供表头的字体格式和表内部数据等样式相关的信息,然后再用DataGrid里提取的数据,生成DataTable和其它必需信息,填充到报表里,
自动调整报表格式
3、做了一个TreeView,很简单;根据报表文件名称,切换左侧TreeView的Item,就加载不同的报表,显示数据。用了一点反射的知识
转载请注明出处: https://www.cnblogs.com/NaughtyCat/p/auto-generate-report.html
第一步:根据 Report Definition Language (RDL) 生成对应的类和命名空间。
1、去 http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition/ 下载ReportDefinition2010.xsd。
注意:ReportDefinition和Visual Studio发布的有个时间差,官网上有ReportDefinition2005版和ReportDefinition2008版。ReportDefinition2005版,VS2008及以后才支持;
ReportDefinition2008版,VS2010及以后支持。2010版,要VS2012以后才支持。我的是VS2010,用ReportDefinition2008版就好。
2、找XML Schema Definition Tool (Xsd.exe),Windows操作系统会自带(微软会自带很多功能强大的exe,要是开源就好了)。For more detail,please refer to:
官网有详细的命令使用说明 https://msdn.microsoft.com/en-us/library/x6c1kb0s(v=vs.110).aspx
Below is my CMD in administator mode:
C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\Bin\\x64>xsd
/c /n:RDLC
/out:C:\\Users\\admin\\Desktop\\RDLCReportResearch
C:\\Users\\admin\\Desktop\\RDLCReportResearch\\ReportDefinition.xsd
完了,生成的是这么个样子(ReportDefinition2005的生成出来有8000行左右,ReportDefinition2008的及以后有10000多行,贴一部分,样子参照下面代码)
using System.Xml.Serialization; /// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition")] [System.Xml.Serialization.XmlRootAttribute(Namespace="http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition", IsNullable=false)] public partial class Report { private object[] itemsField; private ItemsChoiceType80[] itemsElementNameField; private System.Xml.XmlAttribute[] anyAttrField; /// <remarks/> [System.Xml.Serialization.XmlAnyElementAttribute()] [System.Xml.Serialization.XmlElementAttribute("Author", typeof(string))] [System.Xml.Serialization.XmlElementAttribute("AutoRefresh", typeof(uint))] [System.Xml.Serialization.XmlElementAttribute("Body", typeof(BodyType))] [System.Xml.Serialization.XmlElementAttribute("Classes", typeof(ClassesType))] [System.Xml.Serialization.XmlElementAttribute("Code", typeof(string))] [System.Xml.Serialization.XmlElementAttribute("CodeModules", typeof(CodeModulesType))] [System.Xml.Serialization.XmlElementAttribute("ConsumeContainerWhitespace", typeof(bool))]
第二步:创建RDLCGenerator类和TablixRDLCGenerator类
1、根据下载的Report Definition Language(RDL)和一个创建的简单的RDLC文件,知道RDLC文件基本要有哪几部分组成;然后层层嵌套创建就出来了,很简单。
2-1、Tablix是关键数据区,GotReportViewer上面的例子,DynamicMatrix和DynamicTable是根据RDL2005来做的,RDL2008以后,就是一个Tablix:
2-2、Tablix的主要数据区域: TablixHierarchyType CreateTablixColumnHierarchy()和TablixHierarchyType CreateTablixRowHierarchy()
2-3、对于HeaderRow和DataRow关键就在下面的不同。
1 private LocIDStringWithDataTypeAttribute CreateTablixTextRunValue(bool isHeaderCell, string name) 2 { 3 LocIDStringWithDataTypeAttribute v = new LocIDStringWithDataTypeAttribute(); 4 v.Value = isHeaderCell ? name : "=Fields!" + name + ".Value"; 5 v.DataType = StringWithDataTypeAttributeDataType.String; 6 return v; 7 }
2-4、DataSet的名字一定要和ReportDataSource里的名字完全匹配
RdlcGenerator的Read和Write方法比较重要。
/// table + matrix = tablix /// Microsoft 用一个tablix来支持Table(表), Matrix(矩阵) and List(列表)这三种报表项 /// 整合了table和matrix的功能
#region Properties // DataGrid 的DataGridColumn的Header private List<string> headerNames = new List<string>(); public List<string> HeaderNames { get { return headerNames; } } // 对应DataGrid Binding的Path private List<string> fieldNames = new List<string>(); public List<string> FieldNames { get { return fieldNames; } } // 对应DataGrid Column的ActualWdith(因为实际的窗口宽度会重新计算) private List<double> widths = new List<double>(); public List<double> Widths { get { return widths; } } // 如果没有更新过页面设置,用ReportViewer的默认页面设置;否则用最新的页面设置 public PageSettings PageSettings { get; set; } public string Headline { get; set; } public string DataSourceName { get; set; } public string DataSetName { get; set; } #endregion #region Methods // 一层套一层,把xml构造出来 private Report CreateReport() { Report report = new Report(); report.Items = new object[] { CreateDataSources(), CreateDataSets(), CreateBody(), CalcReportWidth(), CreatePage(), }; report.ItemsElementName = new ItemsChoiceType80[] { ItemsChoiceType80.DataSources, ItemsChoiceType80.DataSets, ItemsChoiceType80.Body, ItemsChoiceType80.Width, ItemsChoiceType80.Page, }; return report; } private DataSourcesType CreateDataSources() { DataSourcesType dataSources = new DataSourcesType(); dataSources.DataSource = new DataSourceType[] { CreateDataSource() }; return dataSources; } private DataSourceType CreateDataSource() { DataSourceType dataSource = new DataSourceType(); dataSource.Name = String.IsNullOrEmpty(DataSetName) ? "TBReport" : DataSetName; dataSource.Items = new object[] { CreateDataSourceConnectionProperties() }; return dataSource; } private ConnectionPropertiesType CreateDataSourceConnectionProperties() { ConnectionPropertiesType connectionProperties = new ConnectionPropertiesType(); connectionProperties.Items = new object[] { "System.Data.DataSet", "/* Local Connection */", }; connectionProperties.ItemsElementName = new ItemsChoiceType[] { ItemsChoiceType.DataProvider, ItemsChoiceType.ConnectString, }; return connectionProperties; } private DataSetsType CreateDataSets() { DataSetsType dataSets = new DataSetsType(); dataSets.DataSet = new DataSetType[] { CreateDataSet() }; return dataSets; } // Query暂时就不要了 private DataSetType CreateDataSet() { DataSetType dataSet = new DataSetType(); // DataSetName写死就好 dataSet.Name = "CustomerDataSet"; dataSet.Items = new object[] { CreateDataSetFields(), CreateDataSetQuery(), }; return dataSet; } private FieldsType CreateDataSetFields() { FieldsType fields = new FieldsType(); // DataSet的具体field由DataGrid的Bingding的Path值决定 if ((fieldNames != null) && (fieldNames.Count > 0)) { fields.Field = new FieldType[fieldNames.Count]; for (int index = 0; index < fieldNames.Count; index++) fields.Field[index] = CreateDataSetField(fieldNames[index]); } return fields; } private FieldType CreateDataSetField(string fieldName) { FieldType field = new FieldType(); field.Name = fieldName; field.Items = new object[] { fieldName, // CreateDataSetFieldValue(), }; return field; } // 暂时DataType全部用String private StringWithDataTypeAttribute CreateDataSetFieldValue() { StringWithDataTypeAttribute value = new StringWithDataTypeAttribute(); value.DataType = StringWithDataTypeAttributeDataType.String; return value; } private QueryType CreateDataSetQuery() { QueryType query = new QueryType(); query.Items = new object[] { "TBReport", "/* Local Query */", }; query.ItemsElementName = new ItemsChoiceType1[] { ItemsChoiceType1.DataSourceName, ItemsChoiceType1.CommandText, }; return query; } private BodyType CreateBody() { BodyType body = new BodyType(); body.Items = new object[] { "4.8in", // Height CreateReportItems(), // ReportItems CreateBodyStyle(), }; return body; } private ReportItemsType CreateReportItems() { ReportItemsType reportItems = new ReportItemsType(); // 这是关键数据区域 TablixRdlcGenerator tablixGen = new TablixRdlcGenerator(); tablixGen.ResetHeaderNames(HeaderNames); tablixGen.ResetFieldNames(FieldNames); List<string> tablixColumnWidths; DataGridHelper.CalcTablixColumnWidth(CalcReportWidth(), Widths, out tablixColumnWidths); tablixGen.ResetWidths(tablixColumnWidths); reportItems.Items = new object[] { CreateReportHeadlineTextbox(), tablixGen.CreateTablix() }; return reportItems; } // 创建标题 private TextboxType CreateReportHeadlineTextbox() { TextboxType headlineTextbox = new TextboxType(); headlineTextbox.Name = "headlineTextbox"; string left = (PageSettings == null) ? "2cm" : ((double)PageSettings.Margins.Left / 100.0).ToString() + "in"; string width = (PageSettings == null) ? "17cm" : ((double)(PageSettings.PaperSize.Width - PageSettings.Margins.Left - PageSettings.Margins.Right) / 100.0).ToString() + "in"; headlineTextbox.Items = new object[] { true, true, CreateHeadlineTextboxParagraphs(), left, "0.5cm", "1.0cm", width, CreateHeadlineTextboxStyle() }; headlineTextbox.ItemsElementName = new ItemsChoiceType14[] { ItemsChoiceType14.CanGrow, ItemsChoiceType14.KeepTogether, ItemsChoiceType14.Paragraphs, ItemsChoiceType14.Left, ItemsChoiceType14.Top, ItemsChoiceType14.Height, ItemsChoiceType14.Width, ItemsChoiceType14.Style }; return headlineTextbox; } private ParagraphsType CreateHeadlineTextboxParagraphs() { ParagraphsType headlineParagraphs = new ParagraphsType(); headlineParagraphs.Paragraph = new ParagraphType[] {CreateHeadlineTextboxParagraph()}; return headlineParagraphs; } private ParagraphType CreateHeadlineTextboxParagraph() { ParagraphType pt = new ParagraphType(); pt.Items = new object[] { CreateHeadlineTextRuns(), CreateHeadlineParagraphStyle() }; pt.ItemsElementName = new ItemsChoiceType12[] { ItemsChoiceType12.TextRuns, ItemsChoiceType12.Style, }; return pt; } private TextRunsType CreateHeadlineTextRuns() { TextRunsType trt = new TextRunsType(); trt.TextRun = new TextRunType[] { CreateHeadlineTextRun() }; return trt; } private TextRunType CreateHeadlineTextRun() { TextRunType trt = new TextRunType(); trt.Items = new object[] { CreateHeadLineTextRunValue(), CreateHeadlineTextRunStyle() }; trt.ItemsElementName = new ItemsChoiceType11[] { ItemsChoiceType11.Value, ItemsChoiceType11.Style }; return trt; } private LocIDStringWithDataTypeAttribute CreateHeadLineTextRunValue() { LocIDStringWithDataTypeAttribute value = new LocIDStringWithDataTypeAttribute(); value.Value = (Headline == null) ? "标题" : Headline; value.DataType = StringWithDataTypeAttributeDataType.String; return value; } private StyleType CreateHeadlineTextRunStyle() { StyleType st = new StyleType(); st.Items = new object[] { "宋体", "14pt", "Bold", }; st.ItemsElementName = new ItemsChoiceType4[] { ItemsChoiceType4.FontFamily, ItemsChoiceType4.FontSize, ItemsChoiceType4.FontWeight }; return st; } private StyleType CreateHeadlineParagraphStyle() { StyleType st = new StyleType(); st.Items = new object[] { "Center" }; st.ItemsElementName = new ItemsChoiceType4[] { ItemsChoiceType4.TextAlign }; return st; } private StyleType CreateHeadlineTextboxStyle() { StyleType headlineStyle = new StyleType(); headlineStyle.Items = new object[] { CreateHeadlineTextboxBorder(), "2pt", "2pt", "2pt", "2pt" }; headlineStyle.ItemsElementName = new ItemsChoiceType4[] { ItemsChoiceType4.Border, ItemsChoiceType4.PaddingLeft, ItemsChoiceType4.PaddingRight, ItemsChoiceType4.PaddingTop, ItemsChoiceType4.PaddingBottom }; return headlineStyle; } private BorderType CreateHeadlineTextboxBorder() { BorderType headlineTextboxBorder = new BorderType(); headlineTextboxBorder.Items = new object[] { "None" }; headlineTextboxBorder.ItemsElementName = new ItemsChoiceType2[] { ItemsChoiceType2.Style }; return headlineTextboxBorder; } private StyleType CreateBodyStyle() { return new StyleType(); } /// <summary> /// 设置页面基本属性—页眉、页脚、页宽、页高、左边距、右边距等 /// </summary> private PageType CreatePage() { PageType page = new PageType(); // 根据微软官方文档,PaperSize.Height, PaperSize.Width and Margins的Left, Right, Top, Bottom are in hundredths of an inch. string pageHeight = (PageSettings == null) ? "29.7cm" : ((double)PageSettings.PaperSize.Height / 100.0).ToString() + "in"; string pageWidth = (PageSettings == null) ? "21cm" : ((double)PageSettings.PaperSize.Width / 100.0).ToString() + "in"; string leftMargin = (PageSettings == null) ? "2cm" : ((double)PageSettings.Margins.Left / 100.0).ToString() + "in"; string rightMargin = (PageSettings == null) ? "2cm" : ((double)PageSettings.Margins.Right / 100.0).ToString() + "in"; string topMargin = (PageSettings == null) ? "2cm" : ((double)PageSettings.Margins.Top / 100.0).ToString() + "in"; string bottomMargin = (PageSettings == null) ? "2cm" : ((double)PageSettings.Margins.Bottom / 100.0).ToString() + "in"; // TODO: // 页眉、页脚(后面再做) page.Items = new object[] { //创建Header不能为空 // CreatePageHeader(), pageHeight, pageWidth, leftMargin, rightMargin, topMargin, bottomMargin, "0.13cm", }; page.ItemsElementName = new ItemsChoiceType77[] { // ItemsChoiceType77.PageHeader, ItemsChoiceType77.PageHeight, ItemsChoiceType77.PageWidth, ItemsChoiceType77.LeftMargin, ItemsChoiceType77.RightMargin, ItemsChoiceType77.TopMargin, ItemsChoiceType77.BottomMargin, ItemsChoiceType77.ColumnSpacing }; return page; } /// <summary> /// PageHeader和PageFooter也只是TextRun里Value的数据不一样 /// </summary> /// <returns></returns> private PageSectionType CreatePageHeader() { return new PageSectionType(); } private PageSectionType CreatePageFooter() { return new PageSectionType(); } /// <summary> /// 把Report序列化为流 /// </summary> /// <param name="stream">根据Report序列化好的流</param> public void Write(Stream stream) { Write(stream, CreateReport()); } public void Write(Stream stream, Report report) { new XmlSerializer(typeof(Report)).Serialize(stream, report); } public Report Read(Stream stream) { return (Report)new XmlSerializer(typeof(Report)).Deserialize(stream); } /// <summary> /// 把和DataGrid对应的rdlc模板文件反序列化为Report /// </summary> /// <param name="rdlcModelFilePath">和DataGrid对应的rdlc模板文件</param> /// <returns>反序列化之后的Report</returns> public Report Read(string rdlcModelFilePath) { using (var stream = new FileStream(rdlcModelFilePath, FileMode.Open)) { return Read(stream); } } public void Write(string rdlcModelFilePath) { using (var stream = new FileStream(rdlcModelFilePath, FileMode.OpenOrCreate)) { stream.SetLength(0); Write(stream); } } /// <summary> /// 计算Report的宽度,页宽 - 左边距 - 右边距 /// </summary> /// <returns></returns> public string CalcReportWidth() { string reportWidth = String.Empty; const double size = 100.0; reportWidth = (PageSettings == null) ? "6.5in" : ((double)(PageSettings.PaperSize.Width - PageSettings.Margins.Left - PageSettings.Margins.Right) / size).ToString() + "in"; return reportWidth; }
public class TablixRdlcGenerator { #region Properties // DataGrid 的DataGridColumn的Header private List<string> headerNames = new List<string>(); public List<string> HeaderNames { get { return headerNames; } } // 对应DataGrid Binding的Path private List<string> fieldNames = new List<string>(); public List<string> FieldNames { get { return fieldNames; } } public string DataSetName { get; set; } // 对应DataGrid Column的ActualWidth private List<string> widths = new List<string>(); public List<string> Widths { get { return widths; } } #endregion #region Methods private void ResetValues(List<string> p, List<string> v) { p.Clear(); if (v != null) { p.AddRange(v); } } public void ResetHeaderNames(List<string> hns) { ResetValues(HeaderNames, hns); } public void ResetFieldNames(List<string> fns) { ResetValues(FieldNames, fns); } public void ResetWidths(List<string> widths) { ResetValues(Widths, widths); } /// <summary> /// 矩阵和Table对应的Tablix稍微有些不一样,如对于矩阵,TablixBody里的表头和数据项 /// 一些值会拆分到TablixColumnHierarchy和TablixRowHierarchy里TablixMember--TablixHeader--CellContents--Textbox /// 对于DataGrid我们用最简单的Table就好 /// </summary> /// <returns></returns> public TablixType CreateTablix() { TablixType tablix = new TablixType(); tablix.Name = "dataGridTablix0报表与打印学习总目录