C# 數據庫係統中使用GDI+繪製柱狀圖
在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/具體的品牌數量,即可平均分配每個品牌的數量,再求出具體的坐標即可.
分享文章:https://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點https://blog.csdn.net/eastmount/)
最後更新:2017-04-03 15:21:44