.net Crystal Report dynamic columns
前言:
聽說Crystal Report 官方已經很久沒有維護了
公司在用的版本 也都找不到文件 ((而且公司用的是VB
想到頭就好痛QAQ
正文:
最近在寫一個需求
有一個可選欄位(動態)的Table 要用CR呈現資料
想呈現的樣子如圖:
如果用visual studio設計工具直接拉 可能很難達成以上需求
如果全部用 RAS (Report Application Server) API 寫是可以達到目的
但用程式layout 也是有些麻煩
於是我的腦袋瓜想到把兩個摻在一起做撒尿牛丸
把元件排用設計工具layout完成後
在用RAS API動態設定欄位
在開始之前先說明一下
本文使用環境為
.Net Framework 4.7.2
Crystal Report for .Net 13.0.4000
UI大概長這樣
設計如下:
接著到report的設計頁面用資料庫專家把他加入
然後把report的畫面拉成下圖
接著回到winform
我們想要按下generate後
根據CheckedListBox勾選的內容
動態調整欄位長寬位置等
部分程式如下
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const int PAGE_MARGIN = 400; | |
private void btnGenerate_Click(object sender, EventArgs e) | |
{ | |
//process data | |
DataSet data = new DataSet(); | |
DataTable table = new DataTable("test"); | |
string[] rowData = new string[] { "line1", "line1\nline2", "line1\nline2\nline3", "line1\nline2\nline3\nline4" }; | |
if (cbl_col.CheckedItems.Count < 1) | |
{ | |
MessageBox.Show("Please ensure that you have chosen at least 1 item."); | |
return; | |
} | |
foreach (string name in cbl_col.Items) | |
{ | |
table.Columns.Add(name); | |
} | |
for (int i = 0; i < rowData.Length; i++) | |
{ | |
table.Rows.Add(rowData[i], rowData[(i + 1) % 4], rowData[(i + 2) % 4], rowData[(i + 3) % 4]); | |
} | |
data.Tables.Add(table); | |
//load rpt | |
ReportDocument rpt = new ReportDocument(); | |
rpt.Load(".\\test.rpt"); | |
//set data source | |
rpt.SetDataSource(data); | |
//do setting columns dynamically | |
//get page width | |
int width = rpt.PrintOptions.PageContentWidth - PAGE_MARGIN; | |
//we set each columns' weight to 1 here | |
int totalWeight = cbl_col.CheckedItems.Count; | |
//now, we want to remove columns that user isn't select | |
//so we get the Enumerator of Section which the Section name is 'Section2' & 'Section3' | |
IEnumerator section2Enumerator = rpt.ReportDefinition.Sections["Section2"].ReportObjects.GetEnumerator(); | |
IEnumerator section3Enumerator = rpt.ReportDefinition.Sections["Section3"].ReportObjects.GetEnumerator(); | |
//walk through & remove unused columns | |
while (section2Enumerator.MoveNext()) | |
{ | |
if (section2Enumerator.Current == null) continue; | |
//remove ReportObjects if the name equals database(or data definition) column name | |
if (!cbl_col.CheckedItems.Contains((section2Enumerator.Current as ReportObject).Name.Replace("txt_", ""))) | |
{ | |
CrystalDecisions.ReportAppServer.ReportDefModel.ISCRReportObject obj = rpt.ReportClientDocument.ReportDefController.ReportObjectController | |
.GetAllReportObjects()[(section2Enumerator.Current as ReportObject).Name]; | |
rpt.ReportClientDocument.ReportDefController.ReportObjectController.Remove(obj); | |
} | |
} | |
//do the same thing with Section3 | |
while (section3Enumerator.MoveNext()) | |
{ | |
if (section3Enumerator.Current == null) continue; | |
//remove ReportObjects if the name equals database(or data definition) column name | |
if (!table.Columns.Contains((section3Enumerator.Current as ReportObject).Name.Replace("fd_", ""))) | |
{ | |
CrystalDecisions.ReportAppServer.ReportDefModel.ISCRReportObject obj = rpt.ReportClientDocument.ReportDefController.ReportObjectController | |
.GetReportObjectsByKind(CrystalDecisions.ReportAppServer.ReportDefModel.CrReportObjectKindEnum.crReportObjectKindField)[(section3Enumerator.Current as ReportObject).Name]; | |
rpt.ReportClientDocument.ReportDefController.ReportObjectController.Remove(obj); | |
} | |
} | |
//if we set border in design tool, assume there is data that has different lines between each other | |
//you were found that vertical(left & right) border height is different between each columns | |
//to solve the problem, we need to use line object and set 'ExtendToBottomOfSection' to true | |
//and because our columns are dynamic, we also need to add line object dynamically | |
//get Section3 as ReportDefModel.Section, we're able to add LineObject later | |
CrystalDecisions.ReportAppServer.ReportDefModel.Section section3 = rpt.ReportClientDocument.ReportDefController.ReportDefinition.FindSectionByName("Section3"); | |
//draw line & set columns position | |
int last = 50; | |
CrystalDecisions.ReportAppServer.ReportDefModel.LineObject startLine = new CrystalDecisions.ReportAppServer.ReportDefModel.LineObject(); | |
startLine.LineThickness = 20; | |
startLine.LineColor = 0x0; | |
startLine.LineStyle = CrystalDecisions.ReportAppServer.ReportDefModel.CrLineStyleEnum.crLineStyleSingle; | |
startLine.EnableExtendToBottomOfSection = true; | |
startLine.Left = last; | |
startLine.Right = last; | |
startLine.Bottom = 254; | |
startLine.Top = 0; | |
startLine.SectionName = section3.Name; | |
startLine.EndSectionName = section3.Name; | |
startLine.SectionCode = section3.SectionCode; | |
rpt.ReportClientDocument.ReportDefController.ReportObjectController.Add(startLine, section3); | |
last += 50; | |
for (int i = 0; i < cbl_col.CheckedItems.Count; i++) { | |
int mWidth = width/totalWeight; | |
TextObject mTxt = rpt.ReportDefinition.Sections["Section2"].ReportObjects["txt_" + cbl_col.CheckedItems[i]] as TextObject; | |
FieldObject mDt = rpt.ReportDefinition.Sections["Section3"].ReportObjects["fd_" + cbl_col.CheckedItems[i]] as FieldObject; | |
CrystalDecisions.ReportAppServer.ReportDefModel.LineObject mLine = new CrystalDecisions.ReportAppServer.ReportDefModel.LineObject(); | |
mLine.LineThickness = 20; | |
mLine.LineColor = 0x0; | |
mLine.LineStyle = CrystalDecisions.ReportAppServer.ReportDefModel.CrLineStyleEnum.crLineStyleSingle; | |
mLine.EnableExtendToBottomOfSection = true; | |
mTxt.Left = last; | |
mDt.Left = last; | |
mTxt.Width = mWidth; | |
mDt.Width = mWidth; | |
mLine.Left = last + mWidth; | |
mLine.Right = last + mWidth; | |
mLine.Bottom = 211; | |
mLine.Top = 0; | |
last = last + mTxt.Width + 45; | |
mLine.SectionName = section3.Name; | |
mLine.EndSectionName = section3.Name; | |
mLine.SectionCode = section3.SectionCode; | |
rpt.ReportClientDocument.ReportDefController.ReportObjectController.Add(mLine, section3); | |
} | |
rpt.Refresh(); | |
crystalReportViewer1.ReportSource = rpt; | |
crystalReportViewer1.Refresh(); | |
} |
執行結果如下
有疑問或錯誤的話歡迎指教
留言
張貼留言