ASP.NET 简单的柱形图实现(附带示例)

简介:

对于一些内部系统的项目,各种图表是在所难免的,因为图表可以更加清晰的表达出想看到的数据。

因为之前从来没有做过关于图表的东西,唯一能想到的就是“验证码”,所以应该是一个思路,用GDI去搞。

数据懒着去搞了,记得前些日子在亚航官网查机票,就想到这些数据还挺适合做这个DEMO的,所以就先借用一下亚航的数据喽。

数据大概就是这样子的,日期及价钱。

wKiom1QpAsrDvkkJAAQmKmVvaIY310.jpg

wKiom1QpAtCiVqxCAAGAgHZ4EvA748.jpg

我选了其中“9月27日-10月10日”正好两周的数据作为此次Demo的测试数据。

原理就是跟实现验证码一模一样,通过给<img>标签修改src属性来操作柱形图的变化,src属性导向的页面是另外一个页,非显示柱形图本页。

然后就是如何利用GDI画柱形图。

先上一下效果图:

wKiom1QpA1Hj_oVcAAhfIVxKjgA730.jpg

wKioL1QpA-PSZM72AAhW4tltU4s544.jpg


下面看一下部分代码片段

html代码:

1
2
3
4
5
6
7
8
9
10
11
< body >
     < form  id = "form1"  runat = "server" >
     < div >
         < asp:ImageButton  ID = "img_last"  runat = "server"  ImageUrl = "~/Left.jpg"  AlternateText = "Left"  OnClientClick = "return false;"  />
         < img  id = "img_bar"  src = "GenerateImage.aspx?page_num=0&from=北京&to=清迈"  alt = ""  />
         < asp:ImageButton  ID = "img_next"  runat = "server"  ImageUrl = "~/Right.jpg"  AlternateText = "Right"  OnClientClick = "return false;"  />
     </ div >
     < asp:HiddenField  ID = "hf_pagenum"  runat = "server"  Value = "0"  />
     < asp:HiddenField  ID = "hf_recordnum"  runat = "server"  />
     </ form >
</ body >

js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
         $( function  () {
 
             var  $pagenum = $( "#hf_pagenum" );
             var  $recordnum = $( "#hf_recordnum" );
 
             $( "#img_last" ).click( function  () {
                 if  (parseInt($pagenum.val()) - 1 >= 0) {
                     $pagenum.val(parseInt($pagenum.val()) - 1);
                     $( "#img_bar" ).attr( "src" "GenerateImage.aspx?page_num="  + $pagenum.val() +  "&from=北京&to=清迈" );
                 }
             })
 
             $( "#img_next" ).click( function  () {
                 if  (parseInt($pagenum.val()) + 1 < parseInt($recordnum.val())/7) {
                     $pagenum.val(parseInt($pagenum.val()) + 1);
                     $( "#img_bar" ).attr( "src" "GenerateImage.aspx?page_num="  + $pagenum.val() +  "&from=北京&to=清迈" );
                 }
             })
 
         })

后台代码:

1、一些坐标点的声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
     private  readonly  int  WEEKDAYS = 7; //一周的天数
     private  readonly  int  Y_UNIT_NUM = 10;
     private  readonly  int  CHAR_X_LEFT = 110;
     private  readonly  int  CHAR_X_TOP = 388;
 
     private  readonly  int  CHAR_Y_LEFT = 30;
     private  readonly  int  CHAR_Y_TOP = 79;
 
     private  readonly  int  TITLE_LEFT = 140; //柱形图标题 起始X坐标
     private  readonly  int  TITLE_TOP = 18; //柱形图标题 起始Y坐标
     private  readonly  int  TIME_LEFT = 170; //柱形图日期 起始X坐标
     private  readonly  int  TIME_TOP = 48; //柱形图日期 起始Y坐标
 
     private  readonly  int  GRID_X_LEFT = 150; //背景网格 X左边位移
     private  readonly  int  GRID_X_TOP = 80; //背景网格 X上边位移
     private  readonly  int  GRID_X_BOTTOM = 380; //背景网格 X下边位移
 
     private  readonly  int  GRID_Y_TOP = 110;
     private  readonly  int  GRID_Y_LEFT = 100;
     private  readonly  int  GRID_Y_RIGHT = 580;
 
 
     private  readonly  int  GRID_UNIT_WIDTH = 50; //网格单元宽度
     private  readonly  int  GRID_UNIT_HEIGHT = 30; //网格单元高度
     private  readonly  int  DATA_UNIT_WIDTH = 40; //柱单元宽度
     private  readonly  int  DATA_UNIT_HEIGHT = 30; //柱单元高度

2、筛选当页数据方法

为了是通过点击下一页,上一页按钮,对数据进行筛选,选出当页显示数据进行绘制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     protected  DataSet QueryDisplayRecord( int  pagenum)
     {
         DataSet ds =  new  DataSet();
         DataTable dt =  new  DataTable();
         dt.Columns.Add( new  DataColumn( "date" ));
         dt.Columns.Add( new  DataColumn( "price" ));
         for  ( int  i = 7 * pagenum; i < (goStr.Count >= WEEKDAYS * (pagenum + 1) ? WEEKDAYS * (pagenum + 1) : goStr.Count); i++)
         {
             DataRow dr = dt.NewRow();
             dr[ "date" ] = goStr.Keys.Take(i + 1).Last(); ;
             dr[ "price" ] = goStr.Values.Take(i + 1).Last(); ;
             dt.Rows.Add(dr);
         }
         ds.Tables.Add(dt);
         return  ds;
     }

哦,对了,这里用的是dictionary<string, string>暂时存的模拟数据,正常情况当然要是通过数据库了。

3、生成图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
     protected  void  CreateImage( string  from string  to,  int  pagenum, DataSet ds)
     {
         Font font_Note =  new  System.Drawing.Font( "Arial" , 9, FontStyle.Regular); //x轴,y轴,解释内容字体    小字
         Font font_GraphicName =  new  System.Drawing.Font( "微软雅黑" , 18, FontStyle.Regular); //图表名称 字体      大字
         Font font_GraphicTime =  new  System.Drawing.Font( "微软雅黑" , 14, FontStyle.Regular); //图表时间 字体      大字
         Brush brush_Blue =  new  SolidBrush(Color.Black); //蓝色线条
         if  (ds !=  null )
         {
             int  height = 620, width = 650;
 
             System.Drawing.Bitmap image =  new  System.Drawing.Bitmap(width, height);
             Graphics g = Graphics.FromImage(image);
             g.Clear(Color.White);
             System.Drawing.Drawing2D.LinearGradientBrush brush =  new  System.Drawing.Drawing2D.LinearGradientBrush( new  Rectangle(0, 0, image.Width, image.Height), Color.LightGray, Color.LightGray, 1.2f,  true );
             //调用FillRectangle方法使用指定颜色填充柱形图的内部
             g.FillRectangle(Brushes.WhiteSmoke, 0, 0, width, height);
             Brush brush1 =  new  SolidBrush(Color.Black);
             //应用DrawString方法输出文字信息
             g.DrawString( from  " 至 "  + to +  " 机票分析图 (单位:CNY)" , font_GraphicName, brush_Blue,  new  PointF(TITLE_LEFT, TITLE_TOP));
             //画图片的边框线
             g.DrawRectangle( new  Pen(Color.Black), 0, 0, image.Width - 1, image.Height - 1);
             Pen mypen =  new  Pen(brush, 1);
 
             //绘制横向线条
             for  ( int  i = 0; i < WEEKDAYS; i++)
             {
                 g.DrawLine(mypen, GRID_X_LEFT + GRID_UNIT_WIDTH * i, GRID_X_TOP, GRID_X_LEFT + GRID_UNIT_WIDTH * i, GRID_X_BOTTOM);
             }
             Pen mypen1 =  new  Pen(Color.Black, 2);
             g.DrawLine(mypen1, GRID_X_LEFT - GRID_UNIT_WIDTH, GRID_X_TOP, GRID_X_LEFT - GRID_UNIT_WIDTH, GRID_X_BOTTOM);
             for  ( int  i = 0; i < Y_UNIT_NUM; i++)
             {
                 g.DrawLine(mypen, GRID_Y_LEFT, GRID_Y_TOP + GRID_UNIT_HEIGHT * i, GRID_Y_RIGHT, GRID_Y_TOP + GRID_UNIT_HEIGHT * i);
             }
             g.DrawLine(mypen1, GRID_Y_LEFT, GRID_Y_TOP + GRID_UNIT_HEIGHT * (Y_UNIT_NUM - 1), GRID_Y_RIGHT, GRID_Y_TOP + GRID_UNIT_HEIGHT * (Y_UNIT_NUM - 1));
             //x轴
             for  ( int  i = 0; i < WEEKDAYS; i++)
             {
                 g.DrawString(ds.Tables[0].Rows[i][0].ToString().Substring(ds.Tables[0].Rows[i][0].ToString().LastIndexOf( ',' ) + 1), font_Note, Brushes.Black, CHAR_X_LEFT + GRID_UNIT_WIDTH * i, CHAR_X_TOP);
             }
             //y轴
             string [] money = {  "5000CNY" "4500CNY" "4000CNY" "3500CNY" "3000CNY" "2500CNY" "2000CNY" "1500CNY" "1000CNY" "500CNY"  };
             for  ( int  i = 0; i < Y_UNIT_NUM; i++)
             {
                 g.DrawString(money[i].ToString(), font_Note, Brushes.Black, CHAR_Y_LEFT, CHAR_Y_TOP + DATA_UNIT_HEIGHT * i);
             }
 
             int [] Count =  new  int [WEEKDAYS];
             int  j = 0;
             for  (j = 0; j < WEEKDAYS; j++)
             {
                 Count[j] = Convert.ToInt32(ds.Tables[0].Rows[j][1].ToString());
             }
             //显示柱状效果
             ImageAttributes imgAtt =  new  ImageAttributes();
             imgAtt.SetWrapMode(WrapMode.TileFlipXY); //为了使柱形图片无渐变色效果
             for  ( int  i = 0; i < WEEKDAYS; i++)
             {
                 g.DrawImage(bitmap,  new  Rectangle(GRID_Y_LEFT + GRID_UNIT_WIDTH * i + (GRID_UNIT_WIDTH - DATA_UNIT_WIDTH) / 2, ( int )(380 - (Count[i] > 5000 ? 5000 : Count[i]) * 3F / 50F), 40, ( int )(Count[i] * 3F / 50F)), 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, imgAtt);
             }
             //创建其支持存储区为内存的流
             System.IO.MemoryStream ms =  new  System.IO.MemoryStream();
             //将此图像以指定格式保存到指定流中
             image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
             //清除缓冲区流中的所有内容输出
             Response.ClearContent();
             //设置输出MIME类型
             Response.ContentType =  "image/Gif" ;
             Response.BinaryWrite(ms.ToArray());
         }
         else
         {
             int  height = 380, width = 570;
             System.Drawing.Bitmap image =  new  System.Drawing.Bitmap(width, height);
             Graphics g = Graphics.FromImage(image);
             g.Clear(Color.White);
             //应用DrawString方法输出文字信息
             g.DrawString( "无符合搜索条件数据" , font_GraphicName, brush_Blue,  new  PointF(140, 20));
             //创建其支持存储区为内存的流
             System.IO.MemoryStream ms =  new  System.IO.MemoryStream();
             //将此图像以指定格式保存到指定流中
             image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
             //清除缓冲区流中的所有内容输出
             Response.ClearContent();
             //设置输出MIME类型
             Response.ContentType =  "image/Gif" ;
             Response.BinaryWrite(ms.ToArray());
         }
     }

可能大家看的比较晕,因为全是一些坐标的计算,这个大家就不用太多考虑,就知道图表和柱形的一个思路就行了。

最后总结一下,其实画图指定是离不开一些点在图中位移的计算,这里最好是自己能在草纸上比划一下,设计一下,这样会画的更快一些,同时也不容易被一大堆的数据搞晕。另外一点就是数据这块,柱形的比例搞好就行了,公式大概就是H(格子的实际高度)/h(单位刻度表示的价钱)=X(柱形的高度,我们所有求的)/price(当前柱的实际表示价钱,数据库中存的)。最后也就是X= H*price/h 这样。


想做出更加完美,智能的图表还有许多地方可以改进:

1、y轴展示的刻度我们可以动态变化,根据所查询出数据的最大值进行刻度计算,找出合适的单位刻度

2、x轴可以做一个额外的注释(当然,这里就是星期,也不太用到),但是如果是一些内容的话,可以根据坐标范围做鼠标移上有提示那种。

3、可以把柱形做的再美一点,更加立体一些,当然,这是需要依靠美工的力量。

 

Demo地址:http://down.51cto.com/data/1878284










本文转自 我不会抽烟 51CTO博客,原文链接:http://blog.51cto.com/zhouhongyu1989/1559413,如需转载请自行联系原作者

目录
相关文章
|
6月前
|
存储 开发框架 前端开发
asp.net与asp.net优缺点及示例
asp.net与asp.net优缺点及示例
|
2月前
|
前端开发 数据库连接 数据库
ASP.NETMVC数据库完整CRUD操作示例
ASP.NETMVC数据库完整CRUD操作示例
32 0
|
2月前
|
SQL 开发框架 .NET
ASP.NET Web——GridView完整增删改查示例(全篇幅包含sql脚本)大二结业考试必备技能
ASP.NET Web——GridView完整增删改查示例(全篇幅包含sql脚本)大二结业考试必备技能
33 0
|
开发工具 索引
NET SDK连接阿里云ElasticSearch示例
本文主要演示如何使用elasticsearch-net sdk连接阿里云ElasticSearch实例,通过index的创建进行验证测试。
423 0
NET SDK连接阿里云ElasticSearch示例
|
自然语言处理 API 开发工具
阿里云自然语言处理 .NET 使用示例
本文主要针对目前官方文档中参考Demo使用SDK较老且本身中文打印输出异常的问题,提供包括SDK安装步骤+正常可用示例.NET Demo。
阿里云自然语言处理 .NET 使用示例
【STM32 .Net MF开发板学习-03】TinyGUI绘图示例
.Net Micro Framework官方图形库是WPF,由于目前ST Cortex-M3开发板RAM太小,最大才512K(常见是128K或256k),并且Cortex-M3的CPU主频也不太高,运行WPF图形框架显得过于重了,所以我这边推出了轻量级图形库TinyGUI
575 0
【.Net MF网络开发板研究-03】获取雅虎天气(HttpClient示例)
在网络开发板上实现Http Client,获取雅虎网站的天气信息,并把这些信息在LCD上显示出来。
641 0
|
JSON PHP 数据格式
支付宝预授权.net版接口请求示例
download:支付宝预授权PHP版.zip说明: 本帖是.net开发语言测试支付宝预授权的请求示例,仅供参考!!    一、下载demo: Java版:download:支付宝预授权Java版.zip PHP版:download:支付宝预授权PHP版.
806 0
|
缓存 测试技术 C#
.NET高性能编程之C#玩转CPU高速缓存(附示例)
高并发、低延迟之C#玩转CPU高速缓存(附示例)
2570 0