在前面的一些随笔中,介绍了不少我的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开发框架大的方面的特性进行一个整理,希望能够概括整个框架的一些常用特性,较之前的图形,增加了高级查询模块,统计图表模块等内容。希望大家批评指正。
本文转自博客园伍华聪的博客,原文链接:Winform开发框架之统计图表的实现,如需转载请自行联系原博主。