在前面的一些隨筆中,介紹了不少我的Winform框架的特性,上篇隨筆《Winform開發框架之通用高級查詢模塊》對其中的通用高級模塊進了一個整理說明,本篇繼續介紹Winform開發框架重要的一個特性之統計圖表的實現。統計圖表在很多項目都可能用到,集成到框架中,更方便大家對一些圖表項目的設計理解以及功能的重用。在一般的傳統的框架中,可以采用ZedGraph開源控件或者微軟自帶的MSChart進行圖表設計,DevExpress控件套件有自己的圖表控件,本篇主要介紹基於DevExpress控件的圖表控件進行圖表設計,進一步豐富我的Winform開發框架。
1、普通統計圖表模塊
這里指的普通統計圖表,只是對表某一項目進行單一的統計,可以從餅狀圖、柱狀圖的圖表中體現這些項目各自所占的比例和數值,在我的普通統計圖表模塊中,包括了餅狀圖、柱狀圖和數據表格,這樣更方便對數據進行全面的分析和查看。整個模塊是可以重用的,除了制定字段屬性就可以比較合理的展現出不同分類項目的統計效果了,具體效果圖如下所示。
上面的統計圖表中,還包含了下面兩個功能模塊,如下所示。
通過以上餅圖、柱狀圖以及數據報表,我們可以很清晰地看到各種統計項目的數值和整體直觀的展現圖表了。
由於對這類型的圖表進行了自定義控件的封裝,因此調用非常方便,調用代碼如下所示。
1)綁定統計樹形列表
為了給用戶展示框架(或者項目)支持的報表項目,我們需要在左邊的樹形列表中初始化一些報表項目,具體代碼如下所示。
#region 備件信息統計 this.treeItemDetail.Nodes.Clear(); TreeNode node; node = new TreeNode("備件屬類統計", 0, 0); node.Tag = "ItemBigType"; this.treeItemDetail.Nodes.Add(node); node = new TreeNode("備件類別統計", 0, 0); node.Tag = "ItemType"; this.treeItemDetail.Nodes.Add(node); node = new TreeNode("備件材質統計", 0, 0); node.Tag = "Material"; this.treeItemDetail.Nodes.Add(node); node = new TreeNode("備件名稱統計", 0, 0); node.Tag = "ItemName"; this.treeItemDetail.Nodes.Add(node); node = new TreeNode("所屬庫房統計", 0, 0); node.Tag = "WareHouse"; this.treeItemDetail.Nodes.Add(node); node = new TreeNode("所屬部門統計", 0, 0); node.Tag = "Dept"; this.treeItemDetail.Nodes.Add(node); //自定義輸入字段統計 node = new TreeNode("備件數據動態統計", 0, 0); node.Tag = "Customed"; this.treeItemDetail.Nodes.Add(node); #endregion
上面的樹形列表中,我們給Tag賦值,一般情況下是表字段的名稱,有些特殊的,則采用Customed來表示,我們響應樹形列表控件的操作,根據Tag的不同,切換到不同的報表自定義控件進行展現(包括自定義動態項目統計圖表和普通統計圖表項目)。
由於我們對圖表的展現進行了比較合理的封裝,因此基本上普通的圖表統計項目,只是字段名稱的不同。
對於普通統計圖表項目FrmCategoryReport,這個是一個自定義控件來的,方便動態加載到右邊的展示Panel區域,這樣我們就能根據不同類型的報表動態加載。
創建圖表的代碼如下所示。
private DataTable dt = null; private void BindData() { if (string.IsNullOrEmpty(FieldName)) return; //設置報表標題 this.Text = ReportTitle; this.lblReportTitle.Text = ReportTitle; this.chartPie.Series.Clear(); this.chartBar.Series.Clear(); string where = GetConditionSql(); dt = BLLFactory<ItemDetail>.Instance.GetReportData(FieldName, where); this.gridControl1.DataSource = dt; if (dt != null && dt.Rows.Count > 0) { this.chartPie.DataSource = dt; Series pieSeries = CreateSeries(dt, DevExpress.XtraCharts.ViewType.Pie3D, NumericFormat.Percent); chartPie.Series.Add(pieSeries); chartPie.Legend.Visible = true; PieSeriesLabel label = pieSeries.Label as PieSeriesLabel; ((PiePointOptions)label.PointOptions).PercentOptions.PercentageAccuracy = 4; ((PiePointOptions)label.PointOptions).PercentOptions.ValueAsPercent = true; label.Position = PieSeriesLabelPosition.TwoColumns; //設置餅圖上lable的顯示方式,此方式將獨立出一個列顯示lable (pieSeries.View as DevExpress.XtraCharts.Pie3DSeriesView).ExplodeMode = PieExplodeMode.All; //突出顯示餅塊 (pieSeries.View as DevExpress.XtraCharts.Pie3DSeriesView).ExplodedDistancePercentage = 5; //(pieSeries.View as DevExpress.XtraCharts.PieSeriesView).RuntimeExploding = true; //設置了他,你就可以把你喜歡的餅塊拖出來。。。 this.chartBar.DataSource = dt; chartBar.Series.Add(CreateSeries(dt, DevExpress.XtraCharts.ViewType.Bar, NumericFormat.General)); chartBar.Legend.Visible = false; chartBar.SeriesTemplate.LabelsVisibility = DefaultBoolean.True; } this.xtraTabControl1.SelectedTabPageIndex = 0; }
其中我們注意到,創建圖表的Series對象的方法,我進行了進一步的封裝。
Series pieSeries = CreateSeries(dt, DevExpress.XtraCharts.ViewType.Pie3D, NumericFormat.Percent);
其中CreateSeries方法代碼如下所示。
/// <summary> /// 創建圖表的Series對象,可以指定相應的類型 /// </summary> /// <param name="dt">數據源</param> /// <param name="viewType">圖表類型,如DevExpress.XtraCharts.ViewType.Pie3D,DevExpress.XtraCharts.ViewType.Bar</param> /// <param name="format">顯示格式,如百分比NumericFormat.Percent,NumericFormat.General</param> /// <returns></returns> private Series CreateSeries(DataTable dt, DevExpress.XtraCharts.ViewType viewType, NumericFormat format) { Series series = new Series("Serices1 ", viewType); series.DataSource = dt; series.ArgumentScaleType = ScaleType.Qualitative; series.ArgumentDataMember = "argument"; series.ValueScaleType = ScaleType.Numerical; series.ValueDataMembers.AddRange(new string[] { "datavalue" }); series.PointOptions.PointView = PointView.ArgumentAndValues; series.PointOptions.ValueNumericOptions.Format = format; if (format == NumericFormat.Number) { //series.PointOptions.ValueNumericOptions.Precision = 0; } series.LabelsVisibility = DevExpress.Utils.DefaultBoolean.True;//顯示標注標簽 return series; }
在Winform開發框架中,我試圖對備件倉庫中不同類型的設備進行一個庫存統計,也得到了這類型的圖表如下所示。
2、動態項目統計圖表模塊
有時候,我們對於表里面的數據,可能要對不同類型的內容進行動態的統計,以確定他們各自的比例情況,那么這些動態項目的統計圖表就比較合適了,例如,對於病人資料的管理,我們可能需要統計各種病種所占的比例或者各種職業類型的犯病率,這些不太確定的統計項目,就需要一個能夠支持動態項目的統計圖表進行支撐,對於本Winform框架,為了較好呈現這個類型報表的意義,我選擇了對備件類型所占的比例進行一個統計分析,得到下面的統計圖表,如下所示。
上面的圖表統計,除了能夠根據一些條件進行限定查詢范圍外,還可以對一些預設的統計字段進行動態選取,然后根據字段里面的各種內容(統計項目)進行統計,這樣就可以比較有效的統計出各種類型的數值和比例了。
動態項目的統計,主要是對不同字段,以及對應不同字段的內容進行一個條件分析,把它們的統計數字構造一個數據表格即可進行合理展現,如下部門代碼所示。
string fieldName = fieldItem.Value; int totalCount = BLLFactory<ItemDetail>.Instance.GetRecordCount(where);//計算總人數 foreach (string searchItem in this.lstItems.Items) { string condition = string.Format("{0} like '%{1}%' ", fieldName, searchItem); if (!string.IsNullOrEmpty(where)) { condition += string.Format(" AND {0}", where); } int countValue = BLLFactory<ItemDetail>.Instance.GetRecordCount(condition);//計算總人數 countRepeat += countValue; row = dt.NewRow(); row[0] = searchItem; row[1] = countValue; dt.Rows.Add(row); }
3、多重坐標對比統計圖表模塊
有時候我們可能需要對某年各個月份的數值進行對比統計,已看出各種統計項目的發展趨勢或者對比效果,這就要求可以對多份數據進行合並展現,這種在圖表展現中可以使用多重坐標對比的統計圖表模塊進行展現,如我上篇隨筆《DevExpress控件使用之多重坐標圖形的繪制》就對這類型的圖表統計進行了比較細致的分析和說明,相關的效果圖如下所示。
在Winform框架里面,可以對某一年各月份的出入庫數量進行一個分析,得到下面的統計圖。
以上數據不多,展現可能不太好看,下面我給出我另一個軟件系統的界面,其中對病人的出入院記錄進行一個統計對比分析,統計報表如下所示。
4、打印及導出報表
很多時候,我們可能需要對呈現的報表進行一個打印和導出操作,對於DevExpress的ChartControl控件,打印操作很簡單,你甚至可以用一行代碼進行打印操作。
this.chartControl1.ShowPrintPreview(DevExpress.XtraCharts.Printing.PrintSizeMode.Zoom);
或者增加更復雜的定制操作,代碼如下所示。
private void btnPrint_Click(object sender, EventArgs e) { //this.chartControl1.ShowPrintPreview(DevExpress.XtraCharts.Printing.PrintSizeMode.Zoom); DevExpress.XtraPrintingLinks.CompositeLink compositeLink = new DevExpress.XtraPrintingLinks.CompositeLink(); DevExpress.XtraPrinting.PrintingSystem ps = new DevExpress.XtraPrinting.PrintingSystem(); compositeLink.PrintingSystem = ps; compositeLink.Landscape = true; compositeLink.PaperKind = System.Drawing.Printing.PaperKind.A4; DevExpress.XtraPrinting.PrintableComponentLink link = new DevExpress.XtraPrinting.PrintableComponentLink(ps); ps.PageSettings.Landscape = true; link.Component = this.chartControl1; compositeLink.Links.Add(link); link.CreateDocument(); //建立文檔 ps.PreviewFormEx.Show();//進行預覽 }
對於導出報表,我們 一般要求圖文並茂,純粹導出圖表的圖片或者列表,都是一件很簡單的事情,但是要把它們整合一起,並進行合理的排版,這需要費一點心思才能做好。
首先我們來介紹一下,一般圖表控件都有導出Image圖片類型的操作,DevExpress控件也不例外,它的操作代碼如下所示。
//插入圖片到Excel里面 using (MemoryStream stream = new MemoryStream()) { stream.Position = 0; ChartControl chart = (ChartControl)chartControl1.Clone(); chart.Size = new Size(800, 400); chart.ExportToImage(stream, ImageFormat.Jpeg); //進行存儲操作 //worksheet.Pictures.Add(startRow, 0, stream); }
為了更好的整合幾個圖表和數據報表,我們這里采用了Aspose.Cell控件進行代碼操作,把這些圖表動態整合到一個Excel文檔里面進行導出,全部代碼如下所示。
private void btnExport_Click(object sender, EventArgs e) { try { DataTable dt = this.gridControl1.DataSource as DataTable; if (dt == null || dt.Rows.Count == 0) { MessageDxUtil.ShowTips("沒有數據需要導出!"); return; } string saveDocFile = FileDialogHelper.SaveExcel(string.Format("{0}.xls", ReportTitle), "C:\\"); if (!string.IsNullOrEmpty(saveDocFile)) { Workbook workbook = new Workbook(); Worksheet worksheet = workbook.Worksheets[0]; worksheet.PageSetup.Orientation = PageOrientationType.Landscape;//橫向打印 worksheet.PageSetup.Zoom = 100;//以100%的縮放模式打開 worksheet.PageSetup.PaperSize = PaperSizeType.PaperA4; #region 表頭及說明信息 Range range; Cell cell; string content; int colSpan = 3; range = worksheet.Cells.CreateRange(0, 0, 1, colSpan); range.Merge(); range.RowHeight = 20; range.Style = CreateTitleStyle(workbook); cell = range[0, 0]; cell.PutValue(ReportTitle); range = worksheet.Cells.CreateRange(1, 0, 1, colSpan); range.Merge(); range.RowHeight = 15; cell = range[0, 0]; content = string.Format("統計報表詳細列表如下:"); cell.PutValue(content); #endregion #region 生成報表頭部表格 Style headStyle = CreateStyle(workbook, true); Style normalStyle = CreateStyle(workbook, false); int startRow = 2; int startCol = 0; int index = 0; foreach (DataColumn col in dt.Columns) { range = worksheet.Cells.CreateRange(startRow, index, 2, 1); range.Merge(); range.Style = headStyle; cell = range[0, 0]; cell.PutValue(col.ColumnName); cell.Style = headStyle; index++; } #endregion //寫入數據到Excel startRow = startRow + 2; for (int i = 0; i < dt.Rows.Count; i++) { startCol = 0; for (int j = 0; j < dt.Columns.Count; j++) { DataRow dr = dt.Rows[i]; cell = worksheet.Cells[startRow, startCol]; cell.PutValue(dr[j]); cell.Style = normalStyle; startCol++; } startRow++; } //寫入圖注 startRow += 1;//跳過1行 range = worksheet.Cells.CreateRange(startRow++, 0, 1, colSpan); range.Merge(); range.RowHeight = 15; cell = range[0, 0]; cell.PutValue("以曲線圖展示如下:"); //插入圖片到Excel里面 using (MemoryStream stream = new MemoryStream()) { stream.Position = 0; ChartControl chart = (ChartControl)chartControl1.Clone(); chart.Size = new Size(800, 400); chart.ExportToImage(stream, ImageFormat.Jpeg); worksheet.Pictures.Add(startRow, 0, stream); } workbook.Save(saveDocFile); if (MessageUtil.ShowYesNoAndTips("保存成功,是否打開文件?") == System.Windows.Forms.DialogResult.Yes) { System.Diagnostics.Process.Start(saveDocFile); } } } catch (Exception ex) { LogTextHelper.Error(ex); MessageUtil.ShowError(ex.Message); return; } }
導出Excel的效果如下所示。
5、整體Winform開發框架的特點介紹
下面是我對Winform開發框架大的方面的特性進行一個整理,希望能夠概括整個框架的一些常用特性,較之前的圖形,增加了高級查詢模塊,統計圖表模塊等內容。希望大家批評指正。