程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> RDLC報表之動態生成報表,rdlc報表生成

RDLC報表之動態生成報表,rdlc報表生成

編輯:C#入門知識

RDLC報表之動態生成報表,rdlc報表生成


前段時間,做了RDLC報表,主要是三塊功能:

1、從DataGrid提取(包括最新的增刪改)的數據,自動生成對應的RDLC報表文件(以流的形式駐存在內存中),用ReportViewer類來展示、打印、排版、預覽、分頁

1-1、提供一個提取任意控件數據的通用接口,然後拼接成DataTable這種網狀的格子。DataGrid裡修改、增加、刪除等數據變動,立即同步更新到報表

2、給一個簡單的RDLC模板,提供表頭的字體格式和表內部數據等樣式相關的信息,然後再用DataGrid裡提取的數據,生成DataTable後其它必需信息,填充到報表裡,

自動調整報表格式

3、做了一個TreeView,很簡單;根據報表文件名稱,切換左側TreeView的Item,就加載不同的報表,顯示數據。用了一點反射的知識

 

 

第一步:根據 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))] ReportDefinition.cs

 

第二步:創建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 } CreateTablixTextRunValue

 2-4、DataSet的名字一定要和ReportDataSource裡的名字完全匹配

 

RdlcGenerator的Read和Write方法比較重要。

/// table + matrix = tablix /// Microsoft 用一個tablix來支持Table(表), Matrix(矩陣) and List(列表)這三種報表項 /// 整合了table和matrix的功能 View Code #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; } RdlcGenerator.cs 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"; tablix.Items = new object[] { // 創建TablixCorner不能創建個空的 // CreateTablixCorner(), CreateTablixBody(), CreateTablixColumnHierarchy(), CreateTablixRowHierarchy(), true, true, CreateDataSetName(), // Top, Left, Height, Width可具體調整 // Top, Left ---> Location(距離左上角);Height, Width ---> Size // (Tablix的大小,這個Width不管用,具體是由各個TablixColumn的Width之和決定) "1.8cm", "2cm", "2cm", "17cm", CreateTablixStyle(), }; tablix.ItemsElementName = new ItemsChoiceType73[] { // ItemsChoiceType73.TablixCorner, ItemsChoiceType73.TablixBody, ItemsChoiceType73.TablixColumnHierarchy, ItemsChoiceType73.TablixRowHierarchy, ItemsChoiceType73.RepeatColumnHeaders, ItemsChoiceType73.RepeatRowHeaders, ItemsChoiceType73.DataSetName, ItemsChoiceType73.Top, ItemsChoiceType73.Left, ItemsChoiceType73.Height, ItemsChoiceType73.Width, ItemsChoiceType73.Style }; return tablix; } /// <summary> /// non-essential element, so make it emtpy temprorily /// 看樣子是表頭行,縱向合並的單元格(如縱向兩行合並為一行)等相關的 /// </summary> /// <returns></returns> private TablixCornerType CreateTablixCorner() { return new TablixCornerType(); } private TablixBodyType CreateTablixBody() { TablixBodyType tablixBody = new TablixBodyType(); tablixBody.Items = new object[] { CreateTablixColumns(), CreateTablixRows(), }; return tablixBody; } private void EnumHeaderNames(Action<int> act) { for (int i = 0; i < HeaderNames.Count; i++) { act(i); } } private TablixColumnsType CreateTablixColumns() { TablixColumnsType tablixColumns = new TablixColumnsType(); // 根據DataGridColumns的數量來決定創建幾列,並且每列要把具體的寬度傳進去 tablixColumns.Items = new object[headerNames.Count]; EnumHeaderNames(p => { tablixColumns.Items[p] = CreateTablixColumn(p); }); return tablixColumns; } private TablixColumnType CreateTablixColumn(int index) { // Width of column,應該根據DataGridColumn.Width來具體設定,暫時給個固定值 return new TablixColumnType() { Items = new object[] { Widths[index] } }; } /// <summary> /// 對於DataGrid只應有兩行,一行是Header,一行是數據 /// 如果有求 /// </summary> /// <returns>TablixRowsType</returns> private TablixRowsType CreateTablixRows() { TablixRowsType tablixRows = new TablixRowsType(); tablixRows.Items = new object[] { CreateTablixRowHeader(), CreateTablixRowData(), }; return tablixRows; } private TablixRowType CreateTablixRowType(bool isHeader) { TablixRowType trt = new TablixRowType(); trt.Items = new object[] { "0.23622in", // Default height CreateTablixCells(isHeader), // Header的Cells的內容和Data的Cells的內容應該不同 }; return trt; } /// <summary> /// Tablix Header /// </summary> /// <returns></returns> private TablixRowType CreateTablixRowHeader() { return CreateTablixRowType(true); } private TablixRowType CreateTablixRowData() { return CreateTablixRowType(false); } private TablixCellsType CreateTablixCells(bool isHeaerCell) { TablixCellsType tablixCells = new TablixCellsType(); // 根據DataGridColumns的數量來決定創建幾個Cell, Header應傳DataGridColumn.Header數據 tablixCells.Items = new object[HeaderNames.Count]; EnumHeaderNames(p => { tablixCells.Items[p] = CreateTablixCell(isHeaerCell, p); }); return tablixCells; } private TablixCellType CreateTablixCell(bool isHeaderCell, int index) { TablixCellType tablixCell = new TablixCellType(); // 基本的只要"CellContents"就夠了 tablixCell.Items = new object[] { CreateCellContentes(isHeaderCell, index) }; return tablixCell; } private CellContentsType CreateCellContentes(bool isheaderCell, int index) { CellContentsType cellContents = new CellContentsType(); // 對於DataGrid轉換的rdlc,通常是一個Textbox。具體可以是Chart、Image、Line、Rectangle、Subreport等等 cellContents.Items = new object[] { CreateTablixCellTextbox(isheaderCell, index) }; cellContents.ItemsElementName = new ItemsChoiceType71[] { ItemsChoiceType71.Textbox }; return cellContents; } private TextboxType CreateTablixCellTextbox(bool isHeaderCell, int index) { TextboxType tablixCellTextbox = new TextboxType(); // 對於Header的Textbox可以復雜一點,多些字體、背景顏色等字段的定義 // Data的簡單點//isHeaderCell ? headerNames[index] : tablixCellTextbox.Name = isHeaderCell ? "TB" + fieldNames[index] : fieldNames[index]; tablixCellTextbox.Items = new object[] { true, true, CreateTablixCellTextboxParagraphs(isHeaderCell, isHeaderCell ? headerNames[index] : fieldNames[index]), CreateTablixCellTextboxStyle(), }; tablixCellTextbox.ItemsElementName = new ItemsChoiceType14[] { ItemsChoiceType14.CanGrow, ItemsChoiceType14.KeepTogether, ItemsChoiceType14.Paragraphs, ItemsChoiceType14.Style, }; return tablixCellTextbox; } private ParagraphsType CreateTablixCellTextboxParagraphs(bool isHeaderCell, string name) { ParagraphsType pt = new ParagraphsType(); pt.Paragraph = new ParagraphType[] { CreateTablixCellTextboxParagraph(isHeaderCell, name) }; return pt; } private ParagraphType CreateTablixCellTextboxParagraph(bool isHeaderCell, string name) { ParagraphType pt = new ParagraphType(); pt.Items = new object[] { CreateTablixCellTextboxParagraphTextRuns(isHeaderCell, name), CreateTablixCellTextboxParagraphStyle(isHeaderCell), }; pt.ItemsElementName = new ItemsChoiceType12[] { ItemsChoiceType12.TextRuns, ItemsChoiceType12.Style, }; return pt; } private TextRunsType CreateTablixCellTextboxParagraphTextRuns(bool isHeaderCell, string name) { TextRunsType trt = new TextRunsType(); trt.TextRun = new TextRunType[] { CreateTablixCellTextboxParagraphTextRun(isHeaderCell, name) }; return trt; } private TextRunType CreateTablixCellTextboxParagraphTextRun(bool isHeaderCell, string name) { TextRunType trt = new TextRunType(); trt.Items = new object[] { CreateTablixTextRunValue(isHeaderCell, name), CreateTablixTextRunStyle(isHeaderCell), }; trt.ItemsElementName = new ItemsChoiceType11[] { ItemsChoiceType11.Value, ItemsChoiceType11.Style, }; return trt; } // 數據項和Header的關鍵不一樣就在這個了 private LocIDStringWithDataTypeAttribute CreateTablixTextRunValue(bool isHeaderCell, string name) { LocIDStringWithDataTypeAttribute v = new LocIDStringWithDataTypeAttribute(); v.Value = isHeaderCell ? name : "=Fields!" + name + ".Value"; v.DataType = StringWithDataTypeAttributeDataType.String; return v; } private StyleType CreateTablixTextRunStyle(bool isHeaderCell) { StyleType st = new StyleType(); string fontSize = isHeaderCell ? "11pt" : "10pt"; string FontWeight = isHeaderCell ? "Bold" : "Default"; st.Items = new object[] { "宋體", fontSize, FontWeight, }; st.ItemsElementName = new ItemsChoiceType4[] { ItemsChoiceType4.FontFamily, ItemsChoiceType4.FontSize, ItemsChoiceType4.FontWeight, }; return st; } // 暫時設為表頭行“居中對齊”,數據行“靠左對齊”;後面可具體定制表頭行和數據行的對齊方式 private StyleType CreateTablixCellTextboxParagraphStyle(bool isHeaderCell) { StyleType st = new StyleType(); st.Items = new object[] { isHeaderCell ? "Center" : "Left" }; st.ItemsElementName = new ItemsChoiceType4[] { ItemsChoiceType4.TextAlign }; return st; } // ***************************** // Header的Color和Style可以和數據不同,下面是默認的Sytle,可自定義 //****************************** private StyleType CreateTablixCellTextboxStyle() { StyleType st = new StyleType(); st.Items = new object[] { CreateTablixCellTextboxBorder(), "2pt", "2pt", "2pt", "2pt", }; st.ItemsElementName = new ItemsChoiceType4[] { ItemsChoiceType4.Border, // ItemsChoiceType4.BackgroundColor, 默認數據沒有BackgroundColor ItemsChoiceType4.PaddingLeft, ItemsChoiceType4.PaddingRight, ItemsChoiceType4.PaddingTop, ItemsChoiceType4.PaddingBottom, }; return st; } private BorderType CreateTablixCellTextboxBorder() { BorderType bt = new BorderType(); bt.Items = new object[] { "Black", "Solid", "1pt" }; bt.ItemsElementName = new ItemsChoiceType2[] { ItemsChoiceType2.Color, ItemsChoiceType2.Style, ItemsChoiceType2.Width }; return bt; } /// <summary> /// 按最簡單的來,DataGrid對應的應該是有幾個column創建幾個TablixMember /// </summary> private TablixHierarchyType CreateTablixColumnHierarchy() { return new TablixHierarchyType() { Items = new object[] { CreateTablixColumnMembers() } }; } private TablixMembersType CreateTablixColumnMembers() { TablixMembersType tmts = new TablixMembersType(); tmts.TablixMember = new TablixMemberType[HeaderNames.Count]; EnumHeaderNames(p => { tmts.TablixMember[p] = CreateTablixColumnMember(); }); return tmts; } // DataGrid的Column對應的TablixMember創建一個空的就行 private TablixMemberType CreateTablixColumnMember() { return new TablixMemberType(); } // DataGrid按最簡單的默認的來,即創建2個TablixMember即可 private TablixHierarchyType CreateTablixRowHierarchy() { return new TablixHierarchyType() { Items = new object[] { CreateTablixRowMembers() } }; } private TablixMembersType CreateTablixRowMembers() { TablixMembersType tablixMembers = new TablixMembersType(); tablixMembers.TablixMember = new TablixMemberType[] { CreateTablixRowMember0(), CreateTablixRowMember1(), }; return tablixMembers; } private TablixMemberType CreateTablixRowMember0() { TablixMemberType tmt = new TablixMemberType(); tmt.Items = new object[] { CreateTablixRowMemberKeepWithGroup(), true, }; tmt.ItemsElementName = new ItemsChoiceType72[] { ItemsChoiceType72.KeepWithGroup, ItemsChoiceType72.RepeatOnNewPage, }; return tmt; } private TablixMemberTypeKeepWithGroup CreateTablixRowMemberKeepWithGroup() { return TablixMemberTypeKeepWithGroup.After; } private TablixMemberType CreateTablixRowMember1() { TablixMemberType tmt = new TablixMemberType(); tmt.Items = new object[] { CreateTablixRowMemberGroup() }; tmt.ItemsElementName = new ItemsChoiceType72[] { ItemsChoiceType72.Group }; return tmt; } private GroupType CreateTablixRowMemberGroup() { return new GroupType() { Name = "詳細信息" }; } /// <summary> /// ReportDataSource.Name和RDLC文件的DataSetNamey應保持一致 /// 對於DataGrid構造的報表,可統一固定用"CustormerDataSet"; /// DataSetName不需要作為參數傳進來 /// </summary> /// <returns>DataSet Name</returns> private string CreateDataSetName() { return String.IsNullOrEmpty(DataSetName) ? "CustomerDataSet" : DataSetName; } private StyleType CreateTablixStyle() { StyleType st = new StyleType(); st.Items = new object[] { CreateTablixBorder() }; st.ItemsElementName = new ItemsChoiceType4[] { ItemsChoiceType4.Border }; return st; } // Tablix的外邊框格式 private BorderType CreateTablixBorder() { BorderType bt = new BorderType(); bt.Items = new object[] { "Black", "Solid", "2pt" }; bt.ItemsElementName = new ItemsChoiceType2[] { ItemsChoiceType2.Color, ItemsChoiceType2.Style, ItemsChoiceType2.Width }; return bt; } #endregion } TablixRdlcGenerator.cscs

 

第三步:提取DataGrid的數據

1、主要從DataGrid提取每個Column的Width、BindingPath、Header的Content和每個單元格的數據。數據填充DataTable的Rows, BindingPath填充DataTable的Columns,

Header的Content用來作為報表Tablix的標題行。BindingPath,對於DataTemplate和DataGridHyperlinkColumn不知道咋個取提取數據.

 

2、dataGrid.ScrollIntoView(dataGrid.Items[rowIndex])這個是關鍵。DataGrid用了一個虛擬啥子來著的(名字不重要,原理簡單,計算機領域大量處理性能的都是用這個辦法,

包括所謂的雲啊分布式什麼的),就是復用界面顯示,一個窗口裡能裝下的幾十條RowContainer,每次滾動,人要看到的時候才重新提取新的要顯示的數據。

這樣提取數萬條記錄時,界面不會卡,也不會占用很多內存,每次是要顯示的時候才取幾十條,一點點取。要用,才給,只給需要的那點。

/// <summary> /// DataGrid的轉換器,從DataGrid裡提取出數據源,以及HeaderName、Binding的Path和ActualWidth /// </summary> /// <param name="dataGrid">包含數據的DatGrid</param> /// <param name="dt">DataGrid數據源轉換成的DataTable</param> /// <param name="headerNames">DataGridColumn.Header</param> /// <param name="bindingPaths"> DataGridBoundColumn.Binding.Path</param> public static void DataGridAdapter(this DataGrid dataGrid, DataTable dt, List<string> headerNames, List<string> bindingPaths, List<double> widths) { // 取出DataGridColumn的Header,BingdingPath,ActualWidth為構造rdlc文件准備數據 headerNames.Clear(); bindingPaths.Clear(); widths.Clear(); for (int index = 0; index < dataGrid.Columns.Count; index++) { headerNames.Add(dataGrid.Columns[index].Header as string); widths.Add(dataGrid.Columns[index].ActualWidth); //string tempBindingPath = ((dataGrid.Columns[index] as DataGridBoundColumn).Binding as Binding).Path.Path; string tempBindingPath = GetDataGridColumnBindingPath(dataGrid.Columns[index]); bindingPaths.Add(tempBindingPath); if (String.IsNullOrEmpty(tempBindingPath) == false) dt.Columns.Add(tempBindingPath, typeof(string)); } for (int rowIndex = 0; rowIndex < dataGrid.Items.Count; rowIndex++) { // 要顯示後,才能取到數據 DataGridRow rowContainer = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex); // 因為Peformance問題,EnableRowVirtualization被設置為true,只加載要顯示的數據 // 重新滾動,然後再重用這些DataGridRow if (rowContainer == null) { dataGrid.UpdateLayout(); dataGrid.ScrollIntoView(dataGrid.Items[rowIndex]); rowContainer = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex); } if (rowContainer != null) { DataGridCellsPresenter presenter = DataGridHelper.GetVisualChild<DataGridCellsPresenter>(rowContainer); if (presenter != null) { DataRow dr = dt.NewRow(); bool isLastRowAllEmpty = true; for (int columnIndex = 0; columnIndex < bindingPaths.Count; columnIndex++) { DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex); if (cell != null) { if (cell.Content is TextBlock) { //TODO: DataGridHyperlinkColumn取不到數據 dr[bindingPaths[columnIndex]] = (cell.Content as TextBlock).Text; if (!String.IsNullOrEmpty((cell.Content as TextBlock).Text)) isLastRowAllEmpty = false; } else if (cell.Content is CheckBox) { string value = ((cell.Content as CheckBox).IsChecked == true) ? "是" : "否"; dr[bindingPaths[columnIndex]] = value; } else if (cell.Content is ComboBox) { dr[bindingPaths[columnIndex]] = (cell.Content as ComboBox).Text; if (!String.IsNullOrEmpty((cell.Content as ComboBox).Text)) isLastRowAllEmpty = false; } } } if (dataGrid.CanUserAddRows && (rowIndex == dataGrid.Items.Count - 1)) { // 如果CanUserAddRows被設置為true,只有最後一行的數據都不為空(CheckBox不算作內),才把數據添加到DataTable if (isLastRowAllEmpty) { continue; } } dt.Rows.Add(dr); } } } } 提取DataGrid數據

 

第四步:填充數據

關鍵在設置ReportViewer類的LocalReport.ReportPath 和LocalReport.DataSources這兩項。

/// <summary> /// 報表數據源發生變化時,及時更新顯示報表控件的數據源 /// </summary> /// <param name="reportDataModel">報表數據模型基類</param> public void ResetReportData(ReportDataModel reportDataModel) { if (reportDataModel != null) { reportViewer.Reset(); reportViewer.LocalReport.DataSources.Clear(); reportViewer.Clear(); if (!reportDataModel.IsDataGrid) reportViewer.LocalReport.ReportPath = reportDataModel.RDLCReportPath; else { // 如果是DataGrid轉換成的,直接從內存流裡加載數據 if (reportDataModel.MsDataGrid != null) { reportViewer.LocalReport.LoadReportDefinition(reportDataModel.MsDataGrid); // 用完就釋放掉,流所占用的所有資源 // reportDataModel.MsDataGrid.Dispose(); } } reportViewer.LocalReport.DataSources.Add(reportDataModel.ReportDataSource); reportViewer.RefreshReport(); } } ResetReportData

 

第五步:提供一個ReportHelper類

具體拼接數據以及計算高度等,還有用另一套辦法實現第二個功能。

1、根據DataGrid每列的寬度,按百分比,重新設置每列的寬度。

1 /// <summary> 2 /// 根據DataGrid的Column的Actual Width來設置報表裡對應Tablix的TablixColumn的寬度 3 /// </summary> 4 /// <param name="reportWidth">報表的總寬度</param> 5 /// <param name="widths">DataGrid的Column的Actual Width</param> 6 /// <param name="tablixColumnWidths">重新按百分比計算的TablixColumn的寬度列表</param> 7 public static void CalcTablixColumnWidth(string reportWidth, List<double> widths, out List<string> tablixColumnWidths) 8 { 9 double totalWidth = 0.0; 10 double originalTotalWidth = 0.0; 11 List<double> rateColumnWidth = new List<double>(); 12 string unit = reportWidth.Substring(reportWidth.Length - 2, 2); 13 14 // 取到報表寬度字符串除去單位in或者cm的數值 15 Double.TryParse(reportWidth.Substring(0, reportWidth.Length - 2), out totalWidth); 16 17 18 for (int index = 0; index < widths.Count; index++) 19 originalTotalWidth += widths[index]; 20 21 22 for (int index = 0; index < widths.Count; index++) 23 rateColumnWidth.Add(widths[index] / originalTotalWidth); 24 25 tablixColumnWidths = new List<string>(); 26 tablixColumnWidths.Clear(); 27 for (int index = 0; index < widths.Count; index++) 28 tablixColumnWidths.Add((rateColumnWidth[index] * totalWidth).ToString() + unit); 29 } CalcTablixColumnWidth

2、把內存中的流讀出來,生成對應的RDLC文件,我那裡沒調用。所以設置LocalReport.ReportPath換成reportViewer.LocalReport.LoadReportDefinition(reportDataModel.MsDataGrid);

1 /// <summary> 2 /// 把內存的流生成為rdlc文件 3 /// </summary> 4 /// <param name="rdlc">按rdlc格式構造成功的內存流</param> 5 public static void DumpRdlc(MemoryStream rdlc) 6 { 7 string tempRdlcPath = AppDomain.CurrentDomain.BaseDirectory + @"../../../CommonReport/Templates/GeneratedDataGrid.rdlc"; 8 if (File.Exists(tempRdlcPath)) 9 File.Delete(tempRdlcPath); 10 11 using (FileStream fs = new FileStream(tempRdlcPath, FileMode.Create)) 12 { 13 rdlc.WriteTo(fs); 14 } 15 } DumpRdlc

3、部分調用的代碼——給一個簡單的RDLC模板,以提供表頭的字體格式和表內部數據等樣式相關的信息,然後再用DataGrid裡提取的數據,填充到報表裡

1 /// <summary> 2 /// 將DataGrid的數據抽取出來,轉換成rdlc報表,以實現對提供DataGrid的打印、預覽、分頁和頁面布局等功能的支持 3 /// 但需要提供一個rdlc報表的模板,必須包括頁眉頁腳,至少一列數據和標題,以便拿到數據的表頭的 4 /// style和數據項的style,這一列數據項必須是第一項(且第一項的表頭和數據都完整提供了style) 5 /// </summary> 6 /// <param name="dataGrid">提供數據的DataGrid</param> 7 /// <param name="reportViewer">要加載DataGrid數據的ReportViewer</param> 8 /// <param name="rdlcModelFileName">rdlc模板的完整路徑</param> 9 /// <param name="headline">報表標題</param> 10 public static void Print(this DataGrid dataGrid, CommonReport.Views.ReportViewer reportViewer, string rdlcModelFileName, string headline) 11 { 12 if (!File.Exists(rdlcModelFileName)) return; 13 14 // 從DataGrid對應的rdlc模板裡讀出報表數據來 15 Report report = null; 16 string dataSourceName = DatasetName; 17 dataGrid.UnderRdlcGenProc(reportViewer, headline, gen => 18 { 19 report = gen.Read(rdlcModelFileName); 20 21 // ReportDataSource的Name應該用取DataSet的Name 22 #region 取DataSourceName 23 24 DataGridHelper.ResetRdlcHeadline(report, headline); 25 for (int index = 0; index < report.Items.Length; index++) 26 { 27 if (report.Items[index] is DataSetsType) 28 { 29 DataSetsType dataSets = report.Items[index] as DataSetsType; 30 dataSourceName = dataSets.DataSet[0].Name; 31 break; 32 } 33 } 34 35 #endregion 36 }, (gen, ms, dt) => 37 { 38 // 根據從DataGrid裡提取的數據重新構造rdlc文件 39 RdlcReportAdapter(report, gen.HeaderNames, gen.FieldNames, gen.Widths); 40 gen.Write(ms, report); 41 return new Microsoft.Reporting.WinForms.ReportDataSource(dataSourceName) { Value = dt }; 42 }); 43 } Print

4、打印的關於頁面的一些默認設置(看情況)

1 // 設置默認打印布局模式為“顯示物理頁” 2 reportViewer.SetDisplayMode(DisplayMode.PrintLayout); 3 reportViewer.ZoomMode = ZoomMode.Percent; 4 reportViewer.ZoomPercent = 100; 打印設置

5、TreeView反射那塊——功能三

1 /// <summary> 2 /// TreeView上選擇的項發生變化時,根據所選TreeViewItem的Header信息和Tag裡所存儲的信息,利用反射構造對應報表的數據類實例 3 /// 加載報表模板,調用委托將數據傳到報表的顯示控件上 4 /// </summary> 5 /// <param name="sender"></param> 6 /// <param name="e"></param> 7 private void RdlcTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) 8 { 9 if ((sender != null) && (sender is TreeView)) 10 { 11 if ((sender as TreeView).SelectedItem is TreeViewItem) 12 { 13 TreeViewItem tempItem = (sender as TreeView).SelectedItem as TreeViewItem; 14 if (tempItem.Tag is TreeViewItemDataType) 15 { 16 TreeViewItemDataType tempTreeViewDataType = tempItem.Tag as TreeViewItemDataType; 17 // 報表類型 18 if (tempTreeViewDataType.UserControlType == TreeViewItemDataType.ControlType.Report) 19 { 20 string reportDataModelInstanceName = tempItem.Header + "Model"; 21 Type type = typeof(ReportDataModel); 22 Assembly assembly = type.Assembly; 23 try 24 { 25 ReportDataModel reportDataModelInstance = (ReportDataModel)assembly.CreateInstance(type.Namespace + "." + reportDataModelInstanceName); 26 if (reportDataModelInstance != null) 27 { 28 reportDataModelInstance.RDLCReportPath = (tempItem.Tag as TreeViewItemDataType).FullPath; 29 reportDataModelInstance.InitDataSource(); 30 if (Viewer != null) 31 Viewer.ResetReportData(reportDataModelInstance); 32 } 33 } 34 catch (Exception ex) 35 { 36 MessageBox.Show(ex.Message); 37 } 38 } 39 40 // DataGrid 類型 41 else if (tempTreeViewDataType.UserControlType == TreeViewItemDataType.ControlType.DataGrid) 42 { 43 Type type = this.GetType(); 44 Assembly assembly = type.Assembly; 45 try 46 { 47 UserControl dataGridUserControlInstance = (UserControl)assembly.CreateInstance(type.Namespace + ".DataGrid." + tempItem.Header); 48 } 49 catch (Exception ex) 50 { 51 MessageBox.Show(ex.Message); 52 } 53 } 54 } 55 } 56 } 57 } RdlcTree_SelectedItemChanged

6、通過VisualTreeHelper找到指定類型的子或者父的方法,可在WPF裡通用

/// <summary> /// 找出子Visual的特定類型的Parent /// </summary> /// <typeparam name="T">指定類型</typeparam> /// <param name="child">繼承自Visual的基本控件類型的子Visual</param> /// <returns></returns> public static T GetParent<T>(Visual child) where T : Visual { T parent = default(T); Visual visual = VisualTreeHelper.GetParent(child) as Visual; parent = visual as T; if (parent == null) return GetParent<T>(visual); else return parent; } /// <summary> /// 遍歷取父控件的子Viusal,取到指定類型的子Viusal /// </summary> /// <typeparam name="T">T是Visual或其子類(基本上WPF的控件都是Visual的子類),指定子類型</typeparam> /// <param name="parent">父控件</param> /// <returns>子Viusal</returns> public static T GetVisualChild<T>(Visual parent) where T : Visual { T child = default(T); int numVisuals = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < numVisuals; i++) { Visual visual = (Visual)VisualTreeHelper.GetChild(parent, i); child = visual as T; if (child == null) child = GetVisualChild<T>(visual); else break; } return child; } VisualTreeHelper

7、提供一個數據深拷貝的通用方法(C#類以及除基類型之外,好多都是傳引用,這個是地址,值拷貝不好搞,這個方法直接拷貝流,但是必須類的每個字段都支持序列化)

1 /// <summary> 2 /// 對引用類型的數據——“所有字段都加了Serializable特性,以支持序列化” 3 /// 利用序列化和反序列化實現深度拷貝,即拷貝了堆上的數據,搞了個堆的副本 4 /// 而不是淺拷貝那樣,只是拷貝了一個指向數據堆的內存地址 5 /// 非常實用的小函數,支持所有引用類型數據 6 /// </summary> 7 /// <param name="original">要拷貝的引用類型數據源</param> 8 /// <returns>源數據的副本</returns> 9 public static object DeepColne(Object original) 10 { 11 // 構造一個臨時的內存流 12 using (MemoryStream ms = new MemoryStream()) 13 { 14 // 調用BinaryFormatter來完成復雜的序列化和反序列化工作 15 BinaryFormatter formatter = new BinaryFormatter(); 16 17 // StreamingContext—描述給定的序列化流的源和目標,並提供一個由調用方定義的附加上下文 18 formatter.Context = new StreamingContext(StreamingContextStates.Clone); 19 20 // 把對象圖序列化到內存流,original的每個字段必須標記為可序列化,否則會出錯 21 formatter.Serialize(ms, original); 22 23 // 反序列化之前需要設置流的當前位置為最開始的位置 24 ms.Position = 0; 25 26 // 把內存流反序列化為對象圖,再以基類的形式返回給調用者 27 return formatter.Deserialize(ms); 28 } 29 } DeepColne

 

六、運行效果

1、含有DataGrid或者其它控件的界面

2、點擊打印後,報表生成

附:

1、ReportItems!具體RDLC報表上控件的名稱.Value這個取到報表設計器裡任意項的數據,然後就可在表達式裡進行各種邏輯運算。例如:

= ReportItems!forestryMaintenance.Value + ReportItems!pension.Value + ReportItems!SumPolicy.Value
+ ReportItems!livingExpenses.Value + ReportItems!resettlement.Value

2、合並單元格,縱向和橫向的

這個要分組,具體請搜索網上資源

3、控制每頁都顯示

對於標題,設置KeepWith屬性和Tablix一起出現就好;還有一個辦法,是設置其它的屬性,暫時忘了,網上有

4、XML很重要,據目前所知,微軟的工程文件、WPF、打印、報表、XPS、Office2007以後版本等,XML都是基石。(未完,待續)

5、頁面紙張尺寸(PageSetting裡的一些關於大小的值,單位都是1/100 inch;頁面設置布局排版打印有點麻煩,稍不注意就多出去一點,字體、頁眉、頁腳、邊框、頁邊距等),如下圖:

 6、border style

 

末了,必須感謝和致敬蠟人張前輩:

http://waxdoll.cnblogs.com/archive/2006/02/25/337713.html

2.微軟GotReportViewer官方的案例:

http://www.gotreportviewer.com/(約有20來個,很詳細。有時候會上不了)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved