C# 数据库系统中使用GDI+绘制柱状图

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介:

在C#+SQL Server数据库做系统中,通常需要对数据库中的数据进行绘制图形报表方便经理查看,虽然有很多实用的水晶报表控件和图表控件实现该功能,但我还是想讲讲如何使用GDI绘制简单的柱状图.(推荐大家不要手画,尽量使用已有控件函数绘制)

一.前言

我们在使用C#+SQL Server做的简单应用系统是一个手机销售的系统,所以它有不同手机品牌和不同年份销售手机的利润和数量的柱状图,方便公司经理查看那个月和那种品牌手机销售更好,方便进货增加企业利润.其中它点击"输入年份",查看"2013"年的销售柱状图数据如下所示:(其实效果还行)

二.绘制坐标

下面就首先讲述如何使用GDI+绘制简单的柱状图,首先上面图中我定义为是静态柱状图,因为它X(横坐标)只有12个月份的数据,不会改变,而后面讲解的我定义的动态柱状图会根据手机品牌动态更新数据的,后面将介绍.
首先第一步需要绘制坐标,创建一个"C# Windows窗体应用程序",添加一个简单的button.然后双击button,进入button1_Click(object sender, EventArgs e)事件.添加如下代码.

//定义变量
Graphics graphic;                                      //GDI+绘图图面
SolidBrush Bfill = new SolidBrush(Color.Black);        //定义单色画刷用于填充图形
Pen Rpen = new Pen(Color.Red, 3);                      //创建红色画笔 
//该函数用于绘制坐标
private void CreateTable()
{
    graphic = this.CreateGraphics();                   
    Rectangle rect = new Rectangle(120, 20, 380, 300); //绘制黑色背景
    graphic.FillRectangle(Bfill, rect);                //填充这个矩形
    //Y坐标 (150,40)不变
    graphic.DrawLine(Rpen, new Point(150, 40), new Point(150, 300));   //Y坐标
    graphic.DrawLine(Rpen, new Point(150, 40), new Point(140, 50));    //Y坐标 左箭头
    graphic.DrawLine(Rpen, new Point(150, 40), new Point(160, 50));    //Y坐标 右箭头 
    //X坐标 (470,300)不变
    graphic.DrawLine(Rpen, new Point(150, 300), new Point(470, 300));  //X坐标 
    graphic.DrawLine(Rpen, new Point(460, 290), new Point(470, 300));  //X坐标 上箭头
    graphic.DrawLine(Rpen, new Point(460, 310), new Point(470, 300));  //X坐标 下箭头 
}

在private void button1_Click(object sender, EventArgs e)事件中增加"CreateTable();"代码即可点按钮时击创建坐标,如图:

它具体的坐标如下图所示,其中Rectangle(120, 20, 380, 300)对应Rectangle(int x, int y, int width, int height)表示一个矩形对用起始位置和长宽;使用graphic.DrawRectangle是绘制一个空心的矩形,而graphic.FillRectangle(画刷,矩形)对矩形进行填充.使用graphic.DrawLine(Pen,Point1,Point2)表示画笔,起始坐标xy,终止坐标xy,是绘制直线,其中在绘制箭头需要注意它的相应坐标.其实在草稿纸上设计好在绘制,感觉还是很方便的.

注意:在C#中load事件中是不能画图的,因为Windows的GDI必须在拥有屏幕资源的时候才能有效,FormLoad时窗体还没有绘图资源,所以不能画出.在窗体创建完成后如果再调用FormLoad时间就可以绘制图形.所以我是在点击按钮时创建坐标.

三.静态的柱状图

 下面是绘制静态的柱状图,它的运行效果如下图所示,绘制了12个等高的柱状图:

它的源代码如下,通过调用DrawTable()绘制静态的图形.为什么我定义它为静态的后面将介绍:

//定义变量
Brush Bbrush = Brushes.Blue;                         //创建蓝色画刷 文字
Font myFont = new Font("黑体", 12);                  //创建字体
Font tFont = new Font("宋体", 8);                    //创建字体 显示在直方图上的数量
SolidBrush Wfill = new SolidBrush(Color.White);      //定义单色画刷用于填充图形   
//该函数用于绘制静态报表
private void DrawTable()
{ 
    //输出文字
    graphic.DrawString("月份", myFont, Bbrush, new RectangleF(115, 280, 40, 40));
    graphic.DrawString("月份销售数量", myFont, Bbrush, new RectangleF(260, 40, 200, 40));
    //绘制月份 12个
    for (int i = 1; i <= 12; i++)
    {
        graphic.DrawString(i.ToString(), myFont, Bbrush, 155 + (i-1) * 26, 300);  
    }
    //定义绘制柱状图坐标|宽|高
    int x, y, width, heigh;
    x = 160;                 //X坐标定值=160 Y轴的x坐标为150
    width = 13;              //width坐标=(470-150)/(12*2)=13
    heigh = 200;             //X轴y坐标=300 heigh=200
    y = 100;                 //Y坐标 y=300-200 
            
    for (int i = 0; i < 12; i++)
    {
        //填充图形
        Rectangle rect = new Rectangle(x+i*26, y, width, heigh);  
        graphic.FillRectangle(Wfill, rect);      
        //显示数量
        graphic.DrawString("100", tFont, Brushes.Yellow, x + i * 26, y - 15);
    }
}

这里的有几个地方需要注意,其中26=(470-150)/12表示把X轴分成12个等分,其中每个等分宽为26,再在每个等分绘制一半13的白色矩形.如下图:



四.动态的柱状图

如果动态显示的柱状图,就是获取数据库中具体的数量,在进行绘制图形的基本过程为定义:int num[12]分别记录12个月份中的销售数量,在sumNum+=num[i];计算12个月总的销售数量,通过百分比计算具体的每个月的高度,这样的好处在于当其中一个月的销售数量很大时,会出现那个矩形很高,超出界面范围.
num[i]/sumNum=具体高度/总高度(其中总高度我们在上面设置为heigh=200)
同样,如果在做品牌销售业绩时,如下图所示:

这是销售3中品牌手机"诺基亚"、"三星"、"苹果"的情况,如果想增加一个新的品牌"HTC"时,这是就会显示4条柱状图,因此也需要动态的生成.我们已经求出了它的总宽度为(470-150)=320,再通过320/具体的品牌数量,即可平均分配每个品牌的数量,再求出具体的坐标即可.
分享文章:http://www.cnblogs.com/stg609/archive/2008/03/30/1129221.html
最后补充下获取数据库品牌的代码:

//获取手机品牌信息 (静态生成5个品牌)
private void SelectName()
{
    string sql = "select * from SellList";
    string consqlserver = "Data Source=.;Initial Catalog=TelephoneMS;Integrated Security=True;";
    SqlConnection con = new SqlConnection(consqlserver);     //定义SQL Server连接对象               
    SqlDataAdapter da = new SqlDataAdapter(sql, con);        //数据库命令和数据库连接
    con.Open();
    DataSet ds = new DataSet();                              //声明一个DataSet对象
    da.Fill(ds);                                             //装入数据
    //清零
    for (int i = 0; i < 5; i++) {
        price[i] = 0;
        num[i] = 0;
    }
    try
    {
        //获取实际销售价格总和
        for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
        {
            if (ds.Tables[0].Rows[i]["phonename"].ToString() == "诺基亚")
            {
                //利润 = 实际销售价格 - 进货价格
                price[0] += float.Parse(ds.Tables[0].Rows[i]["sellmoney"].ToString()) -
                    float.Parse(ds.Tables[0].Rows[i]["price"].ToString());
                num[0]++;
            }
            else if (ds.Tables[0].Rows[i]["phonename"].ToString() == "iphone")
            {
                price[1] += float.Parse(ds.Tables[0].Rows[i]["sellmoney"].ToString()) -
                    float.Parse(ds.Tables[0].Rows[i]["price"].ToString());
                num[1]++;
            }
            else if (ds.Tables[0].Rows[i]["phonename"].ToString() == "三星")
            {
                price[2] += float.Parse(ds.Tables[0].Rows[i]["sellmoney"].ToString()) -
                    float.Parse(ds.Tables[0].Rows[i]["price"].ToString());
                num[2]++;
            }
            else if (ds.Tables[0].Rows[i]["phonename"].ToString() == "HTC")
            {
                price[3] += float.Parse(ds.Tables[0].Rows[i]["sellmoney"].ToString()) -
                    float.Parse(ds.Tables[0].Rows[i]["price"].ToString());
                num[3]++;
            }
            else if (ds.Tables[0].Rows[i]["phonename"].ToString() == "OPPO")
            {
                price[4] += float.Parse(ds.Tables[0].Rows[i]["sellmoney"].ToString()) -
                    float.Parse(ds.Tables[0].Rows[i]["price"].ToString());
                num[4]++;
            }
        }
    }
    catch (Exception msg)
    {
        MessageBox.Show(msg.Message);
    }
    finally
    {
        con.Close();
        con.Dispose();
        da.Dispose();
    }
}

总结:回想起来使用GDI+绘制柱状图,确实很蛋疼,也就没继续贴上代码了有具体的控件却不用,但我还是希望该文章对那些不知道在做使用C#做系统中如何生成柱状图报表的有帮助(尽量使用已有的控件).
如果知道如何使用Dundas Chart for .NET\ReportViewer\FormCrystal等制作报表的就当该文章简单帮助你回顾一些GDI+的知识吧!如果文章中有错误或不足的地方,见谅!
(BY:Eastmount 2013-9-14 夜1点http://blog.csdn.net/eastmount/)
相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
7月前
|
SQL 数据库 C#
C# .NET面试系列十一:数据库SQL查询(附建表语句)
#### 第1题 用一条 SQL 语句 查询出每门课都大于80 分的学生姓名 建表语句: ```sql create table tableA ( name varchar(10), kecheng varchar(10), fenshu int(11) ) DEFAULT CHARSET = 'utf8'; ``` 插入数据 ```sql insert into tableA values ('张三', '语文', 81); insert into tableA values ('张三', '数学', 75); insert into tableA values ('李四',
161 2
C# .NET面试系列十一:数据库SQL查询(附建表语句)
|
6天前
|
数据库连接 数据库 C#
Windows下C# 通过ADO.NET方式连接南大通用GBase 8s数据库(下)
本文接续前文,深入讲解了在Windows环境下使用C#和ADO.NET操作南大通用GBase 8s数据库的方法。通过Visual Studio 2022创建项目,添加GBase 8s的DLL引用,并提供了详细的C#代码示例,涵盖数据库连接、表的创建与修改、数据的增删查改等操作,旨在帮助开发者提高数据库管理效率。
|
2月前
|
SQL 缓存 大数据
C#高效处理大数据的批次处理,以及最好的数据库设计
C#高效处理大数据的批次处理,以及最好的数据库设计
67 0
|
3月前
|
SQL 存储 关系型数据库
C#一分钟浅谈:使用 ADO.NET 进行数据库访问
【9月更文挑战第3天】在.NET开发中,与数据库交互至关重要。ADO.NET是Microsoft提供的用于访问关系型数据库的类库,包含连接数据库、执行SQL命令等功能。本文从基础入手,介绍如何使用ADO.NET进行数据库访问,并提供示例代码,同时讨论常见问题及其解决方案,如连接字符串错误、SQL注入风险和资源泄露等,帮助开发者更好地利用ADO.NET提升应用的安全性和稳定性。
296 6
|
4月前
|
关系型数据库 Java MySQL
C#winform中使用SQLite数据库
C#winform中使用SQLite数据库
199 3
C#winform中使用SQLite数据库
|
4月前
|
关系型数据库 MySQL 大数据
C#使用SqlSugar操作MySQL数据库实现简单的增删改查
C#使用SqlSugar操作MySQL数据库实现简单的增删改查
267 2
|
7月前
|
SQL 存储 Oracle
C#利用IDbCommand实现通用数据库脚本执行程序
C#利用IDbCommand实现通用数据库脚本执行程序
|
7月前
|
存储 SQL 数据库
C# 将 Word 转文本存储到数据库并进行管理
C# 将 Word 转文本存储到数据库并进行管理
131 2
|
7月前
|
SQL 存储 关系型数据库
C# .NET面试系列十:数据库概念知识
#### 1. 为什么要一定要设置主键? 设置主键是数据库设计中的一个重要概念,有几个主要原因: 1、唯一性 ```c# 主键必须保证表中的每一行都有唯一的标识。这样可以避免数据冗余和不一致性。如果没有主键或者主键不唯一,就可能出现数据混乱或错误。 ``` 2、查询性能 ```c# 数据库系统通常会使用主键来加速数据检索。主键通常会被索引,这样可以更快速地找到特定行的数据,提高查询效率。 ``` 3、关联性 ```c# 主键常常用于建立表与表之间的关系。在关系数据库中,一个表的主键通常与其他表中的外键建立关联,这种关系对于数据的一致性和完整性非常重要。 ``` 4、数据完
213 1
C# .NET面试系列十:数据库概念知识
|
7月前
|
SQL 存储 数据库连接
C#SQL Server数据库基本操作(增、删、改、查)
C#SQL Server数据库基本操作(增、删、改、查)
356 0