天天看点

devExpress图形生成公共类

最近几个月工作中,查询报表经常会用到【数据 + 图形】的模式,每次生成图形都要写上一堆代码,而且重复率极高。由于DEV了解的也不是很多,每次发现有更好的布局,都要把代码重新迭代一遍,不仅浪费时间,而且开发效率也低下。所以呢,合计写一个公共类,专门用来生成图形的。

一般来说,常用的图形有以下几种:柱图、饼图、线图、雷达图,柱图常用来比较数据,饼图为构成数据,线图用来显示走势,雷达图看分布形势。

说到数据图形,就得提到series,我习惯称他为【维度】;数据图形化除了单维的比较外,还可能存在多维情况,即多个series。拿我做的医疗行业来说,报表中比较全年12个月份,每个月的医院医疗收入,这是单维,但还可能比较每个月里各个科室的收入情况,这就得用到多个series了。月份做X轴,收入做Y轴,当看全院数据时,就一个维度即【全院】,当看各科室数据时,每一个科室即是一个维度。

接下来我们就根据实际来设计一下这个公共类应该如何做。

首先是数据的问题,如果想利用公共类来生成图形,那么数据的存储样式必须规范化,我从实际情况出发,找出工作中统计最多的样式作为模板,然后使用DataTable存储,数据样式如下:

devExpress图形生成公共类
devExpress图形生成公共类

也就是说需要将X轴显示的内容做为列名,Y轴显示的内容作为列值。

有些时候,为了开发效率高一点,SQL语句并不会用decode这种方式来生成上面格式,而是如下面这样:

devExpress图形生成公共类
devExpress图形生成公共类

那么,就需要一个可以将行转列的公共方法来支撑,所以,就有了【ChangeDataTableSingle】这个方法:

/// <summary>
        /// 行转列(一列名称 + 一列数值)
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="int_colIndex1">要转为列的列名索引</param>
        /// <param name="int_colIndex2">对应数值的列名索引</param>
        /// <returns></returns>
        public static DataTable ChangeDataTableSingle(DataTable dt,int int_colIndex1,int int_colIndex2)
        {
            DataTable dt_new = new DataTable();

            string str_colName = dt.Columns[int_colIndex1].Caption;

            //添加新列
            dt_new.Columns.Add("名称", Type.GetType("System.String")); //string型
            foreach (DataRow dr in dt.Rows)
            {
                dt_new.Columns.Add(dr[str_colName].ToString(), Type.GetType("System.Decimal")); //金额型
            }

            DataRow dr_new = dt_new.NewRow();
            dr_new[0] = dt.Columns[int_colIndex2].Caption;
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                dr_new[i + 1] = decimal.Parse(dt.Rows[i][int_colIndex2].ToString());
            }

            dt_new.Rows.Add(dr_new);

            return dt_new;
        }
           

方法比较简单,说一下参数的意思,【dt】代表传入的数据,【int_colIndex1】代表X轴显示信息在当前DataTable中的列索引值,比如我们要显示的月份,对应的是【YEAR_MONTH】列,索引为0;【int_colIndex2】代表Y轴显示的信息,对应列为【CONTROL_EXPENSES】,对应索引为1。所以,调用的方法即是 ChangeDataTableSingle(dt,0,1);

以上的部分为单维度的数据,那么多维度的呢?我们利用下面的方法来生成

/// <summary>
        /// 行转列(一列名称 + 多列数值)
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="int_colIndex1">要转为列的列名索引</param>
        /// <param name="int_colIndex2">对应数值的开始列名索引</param>
        /// <returns></returns>
        public static DataTable ChangeDataTableMuch(DataTable dt,int int_colIndex1, int int_colIndex2)
        {
            DataTable dt1 = new DataTable();

            dt1.Columns.Add("首列名称", Type.GetType("System.String")); //名称列

            foreach (DataRow dr in dt.Rows)
            {
                dt1.Columns.Add(dr[int_colIndex1].ToString(), Type.GetType("System.Decimal")); //数值列
            }

            int int_count = 0;
            foreach (DataColumn dc in dt.Columns)
            {
                if (int_count < int_colIndex2)
                {
                    int_count = int_count + 1;
                    continue;
                }

                DataRow dr1 = dt1.NewRow();
                dr1[0] = dc.Caption;

                for (int j = 0; j < dt.Rows.Count; j++)
                {
                    dr1[j + 1] = decimal.Parse(dt.Rows[j][int_count].ToString());
                }

                dt1.Rows.Add(dr1);

                int_count = int_count + 1;
            }

            return dt1;
        }
           

和单维护的区别在于,【int_colIndex2】的意思不再是单指一个列,而是作为一个起始列,我们以实际数据说明

这是转换前的数据:

devExpress图形生成公共类
devExpress图形生成公共类

【int_colIndex2】的值为1,就代表从列【CONTROL_EXPENSES】开始,和之后的每一个列都对应一个series,这个DataTable就有两个Series

这是转换后的数据:

devExpress图形生成公共类
devExpress图形生成公共类

注意:处理好的数据,一行代表一个Series

数据处理好后,接下来就要生成图形了,上面说过,有可能存在多个Series,那怎么告诉ChartControl,我们要生成多维呢?答案是利用List存储传参。先定义一个类【GraphClass】,只有一个属性,用于存储Series的名称,代码如下:

public class GraphClass
    {
        private string str_colName;

        public string Str_colName
        {
            get { return str_colName; }
            set { str_colName = value; }
        }
    }
           

如果是单维图形,就直接实例化类,并加入List:

List<GraphClass> list_gc = new List<GraphClass>();
GraphClass gc1 = new GraphClass();
gc1.Str_colName = "统筹定额";
list_gc.Add(gc1);
           

如果是多维图形,以DataTable做循环,然后实例化GraphClass,加入List

通过上面的两步,基础信息就准备好了,最后我们来生成图形,以线图为例,先入代码

//形成线性图
        public static void CreateLinear(ChartControl cc, string str_title, DataTable dt, List<GraphClass> list_gc, int int_colIndex)
        {
            cc.Series.Clear();
            cc.Titles[0].Text = str_title;
            int int_count = 0;

            List<Series> list = new List<Series>();
            foreach (GraphClass gc in list_gc)
            {
                Series series = CreateLinearSeries1(gc.Str_colName, ViewType.Line, dt, int_count, int_colIndex);
                if (series == null)
                {
                    continue;
                }

                list.Add(series);

                int_count = int_count + 1;

                //显示数值
                cc.Legend.Visible = true;
                series.PointOptions.PointView = PointView.Values;//设置线图上的lable显示 文字/比率值
                series.LabelsVisibility = DevExpress.Utils.DefaultBoolean.True;
                series.LegendPointOptions.PointView = PointView.Argument; //用于显示右上角信息
            }

            cc.Series.AddRange(list.ToArray());
            cc.SeriesTemplate.LabelsVisibility = DefaultBoolean.True;
        }

        //创建线图series1
        private static Series CreateLinearSeries1(string caption, ViewType viewType, DataTable dt, int rowIndex, int int_colIndex)
        {
            if (dt.Rows.Count - 1 < rowIndex)
            {
                return null;
            }

            Series series = new Series(caption, viewType);
            for (int i = int_colIndex; i < dt.Columns.Count; i++)
            {
                string argument = dt.Columns[i].ColumnName;//参数名称
                string value = dt.Rows[rowIndex][i].ToString();//参数值
                series.Points.Add(new SeriesPoint(argument, value));
            }

            //必须设置ArgumentScaleType的类型,否则显示会转换为日期格式,导致不是希望的格式显示
            //也就是说,显示字符串的参数,必须设置类型为ScaleType.Qualitative
            series.ArgumentScaleType = ScaleType.Qualitative;
            series.LabelsVisibility = DevExpress.Utils.DefaultBoolean.True;//显示标注标签

            return series;
        }
           

可以看到,共有两个方法,第一个用于生成图形基础代码,第二个为生成Series数据的方法。

我们先说第一个方法的参数:

ChartControl cc:代表要显示图形的控件名称,没啥说的

string str_title:代表图形的显示名称,如:医院全年各月收入走势图

DataTable dt:传入的数据

List<GraphClass> list_gc:存储Series的集合

int int_colIndex:这个是重点,代表数据列的起始索引位置

第二个方法的参数:

string caption:为Series的名称

ViewType viewType:代表是什么图形,线图就是【Line】,饼图对应【Pie】,柱图对应【Bar】等等,可以在ChartControl设置中看到

DataTable dt:与第一个方法一样,数据信息

int rowIndex:这个是对应的Series索引,对应的就是dt的行索引

int int_colIndex:与第一个方法一致

然后,还要说一下第一方法中对于ChartControl的一些使用,我们在前端设置界面可以看到很多属性,像Chart Titles、Points Labels等等,这些在前台调整起来都很简单,但是用代码调整时,就很头疼,不知道代码怎么写,用.也点不出来,比如这个设置((PieSeriesLabel)series.Label).Position = PieSeriesLabelPosition.Inside,作用是将数值显示在饼图的块中,以减少占用外部的空间,当时找了四个小时,最后在帮助文档中看到的。所以说,学习DEV,最好还是下载一份官方的帮助文档。

最后,将结果展现一下吧

线图:

devExpress图形生成公共类
devExpress图形生成公共类

饼环图:

devExpress图形生成公共类
devExpress图形生成公共类

饼图:

devExpress图形生成公共类
devExpress图形生成公共类

柱图:

devExpress图形生成公共类

雷达图:

devExpress图形生成公共类