以前一直使用各類報表工具,如RDLC、DevExpress套件的XtraReport報表,在以前一些隨筆也有介紹,最近接觸銳浪的Grid++報表,作了一些測試例子和輔助類來處理報表內容,以爲仍是很不錯的,特別是它的做者提供了不少報表的設計模板案例,功能仍是很是強大的。試着用來作一些簡單的報表,測試下功能,發現常規的二維表、套打、條形碼二維碼等我關注的功能都有,是一個比較強大的報表控件,本篇隨筆主要介紹在Winform開發中使用Grid++報表設計報表模板,以及綁定數據的處理過程。數據庫
這個報表系統,報表模板提供了不少案例,咱們能夠大概瀏覽下其功能。工具
它對應在相應的文件目錄裏面,咱們能夠逐一查看了解下,感受提供這麼多報表仍是很讚的,咱們能夠參考着來用,很是好。測試
整個報表主要是基於現有數據進行一個報表的模板設計的,若是要預覽效果,咱們通常是須要綁定現有的數據,能夠從各類數據庫提供數據源,而後設計報表模板,進行實時的數據和格式查看及調整。spa
空白的報表模板大概以下所示,包含頁眉頁腳,以及明細表格的內容。設計
根據它的教程,模仿着簡單的作了一個報表,也主要是設計報表格式的調整,和數據源的處理的關係,咱們作一個兩個報表就能夠很快上手了。3d
爲了動態的加入咱們表格所須要的列,咱們能夠經過數據庫裏面的字段進行加入,首先提供數據源,指定咱們具體的表便可(若是是自定義的信息,則能夠手工添加字段)code
這個裏面就是配置不一樣的數據庫數據源了orm
如SQLServer數據庫的配置信息以下。對象
爲了方便,咱們能夠利用案例的Access數據庫,也就是Northwind.mdb來測試咱們的報表,弄好這些咱們指定對應的數據表數據便可。blog
這裏面配置好數據庫表信息後,咱們就能夠用它生成相關的字段和對應的列信息了
修改列的表頭,讓它符合中文的表頭列,以下所示。
咱們在頁腳出,加入了打印時間,頁碼的一些系統變量,具體操做就是添加一個綜合文本,而後在內容裏面插入指定的域內容便可,以下所示
預覽報表,咱們就能夠看到具體的報表格式顯示了。
經過上面的操做,感受生成一個報表仍是很方便的,接着我有根據須要作了一個二維碼的報表顯示,方便打印資產標籤。
綁定數據源顯示的報表視圖以下所示,看起來仍是蠻好的。
通常咱們綁定數據源,有的時候能夠直接指定數據庫鏈接,有時候能夠綁定具體的數據列表,如DataTable或者List<T>這樣的數據源,不一樣的方式報表控件的代碼綁定不一樣。
直接綁定數據表的路徑以下所示。
/// <summary> /// 普通鏈接數據庫的例子-打印預覽 /// </summary> private void btnNormalDatabase_Click(object sender, EventArgs e) { Report = new GridppReport(); string reportPath = Path.Combine(Application.StartupPath, "Reports\\testgrid++.grf"); string dbPath = Path.Combine(Application.StartupPath, "Data\\NorthWind.mdb"); //從對應文件中載入報表模板數據 Report.LoadFromFile(reportPath); //設置與數據源的鏈接串,由於在設計時指定的數據庫路徑是絕對路徑。 if (Report.DetailGrid != null) { string connstr = Utility.GetDatabaseConnectionString(dbPath); Report.DetailGrid.Recordset.ConnectionString = connstr; } Report.PrintPreview(true); }
而若是須要綁定和數據庫無關的動態數據源,那麼就須要經過控件的FetchRecord進行處理了,以下代碼所示。
Report.FetchRecord += new _IGridppReportEvents_FetchRecordEventHandler(ReportFetchRecord);
經過這樣咱們增長每個對應的列單元格信息,以下是隨帶案例所示
//在C#中一次填入一條記錄不能成功,只能使用一次將記錄所有填充完的方式 private void ReportFetchRecord() { //將所有記錄一次填入 Report.DetailGrid.Recordset.Append(); FillRecord1(); Report.DetailGrid.Recordset.Post(); Report.DetailGrid.Recordset.Append(); FillRecord2(); Report.DetailGrid.Recordset.Post(); Report.DetailGrid.Recordset.Append(); FillRecord3(); Report.DetailGrid.Recordset.Post(); } private void FillRecord1() { C1Field.AsString = "A"; I1Field.AsInteger = 1; F1Field.AsFloat = 1.01; } private void FillRecord2() { C1Field.AsString = "B"; I1Field.AsInteger = 2; F1Field.AsFloat = 1.02; } private void FillRecord3() { C1Field.AsString = "C"; I1Field.AsInteger = 3; F1Field.AsFloat = 1.03; }
這樣處理確定很麻煩,咱們常規作法是弄一個輔助類,來處理DataTable和List<T>等這樣類型數據的動態增長操做。
/// <summary> /// 綁定實體類集合的例子-打印預覽 /// </summary> private void btnBindList_Click(object sender, EventArgs e) { Report = new GridppReport(); //從對應文件中載入報表模板數據 string reportPath = Path.Combine(Application.StartupPath, "Reports\\testList.grf"); Report.LoadFromFile(reportPath); Report.FetchRecord += ReportList_FetchRecord; Report.PrintPreview(true); } /// <summary> /// 綁定DataTable的例子-打印預覽 /// </summary> private void btnBindDatatable_Click(object sender, EventArgs e) { Report = new GridppReport(); //從對應文件中載入報表模板數據 string reportPath = Path.Combine(Application.StartupPath, "Reports\\testList.grf"); Report.LoadFromFile(reportPath); Report.FetchRecord += ReportList_FetchRecord2; Report.PrintPreview(true); } private void ReportList_FetchRecord() { List<ProductInfo> list = BLLFactory<Product>.Instance.GetAll(); GridReportHelper.FillRecordToReport<ProductInfo>(Report, list); } private void ReportList_FetchRecord2() { var dataTable = BLLFactory<Product>.Instance.GetAllToDataTable(); GridReportHelper.FillRecordToReport(Report, dataTable); }
其中輔助類 GridReportHelper 代碼以下所示。
/// <summary> /// Gird++報表的輔助類 /// </summary> public class GridReportHelper { private struct MatchFieldPairType { public IGRField grField; public int MatchColumnIndex; } /// <summary> /// 將 DataReader 的數據轉儲到 Grid++Report 的數據集中 /// </summary> /// <param name="Report">報表對象</param> /// <param name="dr">DataReader對象</param> public static void FillRecordToReport(IGridppReport Report, IDataReader dr) { MatchFieldPairType[] MatchFieldPairs = new MatchFieldPairType[Math.Min(Report.DetailGrid.Recordset.Fields.Count, dr.FieldCount)]; //根據字段名稱與列名稱進行匹配,創建DataReader字段與Grid++Report記錄集的字段之間的對應關係 int MatchFieldCount = 0; for (int i = 0; i < dr.FieldCount; ++i) { foreach (IGRField fld in Report.DetailGrid.Recordset.Fields) { if (string.Compare(fld.RunningDBField, dr.GetName(i), true) == 0) { MatchFieldPairs[MatchFieldCount].grField = fld; MatchFieldPairs[MatchFieldCount].MatchColumnIndex = i; ++MatchFieldCount; break; } } } // 將 DataReader 中的每一條記錄轉儲到Grid++Report 的數據集中去 while (dr.Read()) { Report.DetailGrid.Recordset.Append(); for (int i = 0; i < MatchFieldCount; ++i) { var columnIndex = MatchFieldPairs[i].MatchColumnIndex; if (!dr.IsDBNull(columnIndex)) { MatchFieldPairs[i].grField.Value = dr.GetValue(columnIndex); } } Report.DetailGrid.Recordset.Post(); } } /// <summary> /// 將 DataTable 的數據轉儲到 Grid++Report 的數據集中 /// </summary> /// <param name="Report">報表對象</param> /// <param name="dt">DataTable對象</param> public static void FillRecordToReport(IGridppReport Report, DataTable dt) { MatchFieldPairType[] MatchFieldPairs = new MatchFieldPairType[Math.Min(Report.DetailGrid.Recordset.Fields.Count, dt.Columns.Count)]; //根據字段名稱與列名稱進行匹配,創建DataReader字段與Grid++Report記錄集的字段之間的對應關係 int MatchFieldCount = 0; for (int i = 0; i < dt.Columns.Count; ++i) { foreach (IGRField fld in Report.DetailGrid.Recordset.Fields) { if (string.Compare(fld.Name, dt.Columns[i].ColumnName, true) == 0) { MatchFieldPairs[MatchFieldCount].grField = fld; MatchFieldPairs[MatchFieldCount].MatchColumnIndex = i; ++MatchFieldCount; break; } } } // 將 DataTable 中的每一條記錄轉儲到 Grid++Report 的數據集中去 foreach (DataRow dr in dt.Rows) { Report.DetailGrid.Recordset.Append(); for (int i = 0; i < MatchFieldCount; ++i) { var columnIndex = MatchFieldPairs[i].MatchColumnIndex; if (!dr.IsNull(columnIndex)) { MatchFieldPairs[i].grField.Value = dr[columnIndex]; } } Report.DetailGrid.Recordset.Post(); } } /// <summary> /// List加載數據集 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="Report">報表對象</param> /// <param name="list">列表數據</param> public static void FillRecordToReport<T>(IGridppReport Report, List<T> list) { Type type = typeof(T); //反射類型 MatchFieldPairType[] MatchFieldPairs = new MatchFieldPairType[Math.Min(Report.DetailGrid.Recordset.Fields.Count, type.GetProperties().Length)]; //根據字段名稱與列名稱進行匹配,創建字段與Grid++Report記錄集的字段之間的對應關係 int MatchFieldCount = 0; int i = 0; MemberInfo[] members = type.GetMembers(); foreach (MemberInfo memberInfo in members) { foreach (IGRField fld in Report.DetailGrid.Recordset.Fields) { if (string.Compare(fld.Name, memberInfo.Name, true) == 0) { MatchFieldPairs[MatchFieldCount].grField = fld; MatchFieldPairs[MatchFieldCount].MatchColumnIndex = i; ++MatchFieldCount; break; } } ++i; } // 將 DataTable 中的每一條記錄轉儲到 Grid++Report 的數據集中去 foreach (T t in list) { Report.DetailGrid.Recordset.Append(); for (i = 0; i < MatchFieldCount; ++i) { object objValue = GetPropertyValue(t, MatchFieldPairs[i].grField.Name); if (objValue != null) { MatchFieldPairs[i].grField.Value = objValue; } } Report.DetailGrid.Recordset.Post(); } } /// <summary> /// 獲取對象實例的屬性值 /// </summary> /// <param name="obj">對象實例</param> /// <param name="name">屬性名稱</param> /// <returns></returns> public static object GetPropertyValue(object obj, string name) { //這個沒法獲取基類 //PropertyInfo fieldInfo = obj.GetType().GetProperty(name, bf); //return fieldInfo.GetValue(obj, null); //下面方法能夠獲取基類屬性 object result = null; foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(obj)) { if (prop.Name == name) { result = prop.GetValue(obj); } } return result; } }
綁定數據的報表效果以下所示
導出報表爲PDF也是比較常規的操做,這個報表控件也能夠實現PDF等格式文件的導出,以下所示。
private void btnExportPdf_Click(object sender, EventArgs e) { List<ProductInfo> list = BLLFactory<Product>.Instance.GetAll(); //從對應文件中載入報表模板數據 string reportPath = Path.Combine(Application.StartupPath, "Reports\\testList.grf"); GridExportHelper helper = new GridExportHelper(reportPath); string fileName = "d:\\my.pdf"; var succeeded = helper.ExportPdf(list, fileName); if(succeeded) { Process.Start(fileName); } }
以上就是利用這個報表控件作的一些功能測試和輔助類封裝,方便使用。