跳到主要內容

用ZedGraph畫統計圖

Update: 沒想到這篇居然變成Google搜尋ZedGraph第一篇中文網頁,不過還是誠心建議用Windows上的C#先看一下免費的圖表元件:Microsoft Chart Controls,除非你非得用.Net 2.0(Windows 2000)或是用Mono。 BTW,我並不想成為微軟MVP,所以本Blog並不是有問必答的喲^_^

才剛貼完上一篇,馬上就有位朋友丟過來一個LGPL Open Source元件的網址:ZedGraph

參考:A flexible charting library for .NET




基本上,照著Use RenderMode.RawImage in a web page,應該可以做出來,只要注意把ZedGraph.dll和ZedGraph.Web.dll都複製到bin下即可。我是偷懶直接下載zedgraph_web_sample_v5.1.2.zip,用檔案系統網站開啟,再把那2個dll丟到bin下測試。



範例裏有三個目錄,我們只關注ImageTagCS和RawModeCS。原來以為ImageTagCS做成WebControl,只要一個檔案即時render比較好,但我發現錯了:會不斷產生暫存影像檔。
所以比較好的做法與ProEssentials一樣,必須用另一支aspx產生影像,也就是RawModeCS裏的做法。

從頭來做一次好了,先開一個新的Asp.Net檔案系統網站,再開bin目錄,將ZedGraph.dll和ZedGraph.Web.dll都複製到bin下。

接著加入一個新WebForm,叫zedgraph.aspx


在Default.aspx裏拉進一個button和一個Image物件,在button上double click,在form標籤內應該是
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="查詢" />
<asp:Image ID="Image1" runat="server" ImageUrl="zedgraph.aspx" />


在Default.aspx.cs裏
protected void Button1_Click(object sender, EventArgs e)
{
PointPairList list1 = new PointPairList();
PointPairList list2 = new PointPairList();
PointPairList list3 = new PointPairList();
Random rand = new Random();

for ( double x=0; x<5; x+=1.0 )
{
double y = rand.NextDouble() * 1000;
double y2 = rand.NextDouble() * 1000;
double y3 = rand.NextDouble() * 1000;
list.Add( x, y );
list2.Add( x, y2 );
list3.Add( x, y3 );
}
Session["list1"] = list1;
Session["list2"] = list2;
Session["list3"] = list3;
//為避免cache,若使用AJAX一定要加
Image1.ImageUrl = "zedgraph.aspx?xxx="+DateTime.Now.Millisecond;
}

打開zedgraph.aspx,刪除原有內容,輸入
<%@ Page Language="c#" Inherits="ZG1.zedgraph" CodeFile="zedgraph.aspx.cs" %>
<%@ Register TagPrefix="zgw" Namespace="ZedGraph.Web" Assembly="ZedGraph.Web" %>
<zgw:zedgraphweb id="ZedGraphWeb1" runat="server" width="800" height="450" rendermode="RawImage" onrendergraph="OnRenderGraph"></zgw:zedgraphweb>

在zedgraph.aspx.cs裏加入
protected void OnRenderGraph(ZedGraphWeb zgw, Graphics g, MasterPane masterPane)
{
// Get the GraphPane so we can work with it
GraphPane myPane = masterPane[0];
myPane.Title.Text = "電腦統計圖";
myPane.XAxis.Title.Text = "部門";
myPane.YAxis.Title.Text = "台數";
// 這裏可視情況改成由DataTable讀入資料,
// 將DataTable從前一頁利用Session傳入
PointPairList list1 = (PointPairList)Session["list1"];
PointPairList list2 = (PointPairList)Session["list2"];
PointPairList list3 = (PointPairList)Session["list3"];

BarItem myCurve = myPane.AddBar("桌機", list1, Color.Blue);
myCurve.Bar.Fill = new Fill(Color.Blue, Color.White, Color.Blue);
BarItem myCurve2 = myPane.AddBar("筆電", list2, Color.Red);
myCurve2.Bar.Fill = new Fill(Color.Red, Color.White, Color.Red);
BarItem myCurve3 = myPane.AddBar("印表機", list3, Color.Green);
myCurve3.Bar.Fill = new Fill(Color.Green, Color.White, Color.Green);

myPane.XAxis.MajorTic.IsBetweenLabels = true;
string[] labels = { "行政部", "會計部", "資訊部", "業務部", "研發部" };
myPane.XAxis.Scale.TextLabels = labels;
myPane.XAxis.Type = AxisType.Text;
myPane.Fill = new Fill(Color.White, Color.FromArgb(200, 200, 255), 45.0f);
myPane.Chart.Fill = new Fill(Color.White, Color.LightGoldenrodYellow, 45.0f);

masterPane.AxisChange(g);
//產生每個Bar上的數量(Label),原範例沒有喲
BarItem.CreateBarLabels(myPane, false, "f0");
}

再執行 Default.aspx,執行結果:


重點在於把值利用Session傳入,以及zedgraph的事件


如果像鳥毅一樣運氣太好,遇到browser cache圖怎麼辦?尤其是放在UpdatePanel內更容易發生,最簡單的方法就是在每次查詢(button click)事件處理內加上一條:
Image1.ImageUrl = "zedgraph.aspx?xxx="+DateTime.Now.Millisecond;
這樣應該就沒問題了。

留言

Unknown寫道…
可以請較一下每個BAR的寬度是依圖形總寬度平均的嗎?
我現在想只作一個BAR 寬度約佔整個圖表的80%,可是我找不到怎麼去修改BarWidth的方法
方便的話可以指點迷津嗎? 先謝謝了~~~
鳥毅寫道…
我猜是 這個啦,我在家用Ubuntu,無開發環境,請見諒。
匿名表示…
不好意思,我最近使用這個控制項時發生了幾項問題
就是我利用大大的方法,但是抓不到資料

在default.aspx中
我是想利用點選一個按鈕後,將現存label.text的資料抓出來,然後
在default.aspx.vb中
將這些抓取道的資料寫到list1,2,3中

最後再在zedgraph.aspx中利用session
這邊的方法與大大的相同

但最後都抓不到資料值 所以圖都是空的
能否請大大指導一下?
鳥毅寫道…
1. 我今天大過了,感謝你的關心。
2. 看不到程式很難除錯耶...不過我建議你先把target指向另一支aspx程式,確定list 1, 2, 3都有值之後,才對zedgraph.aspx除錯。
匿名表示…
妳好,我現在有點亂了
我剛剛又將程式寫了一下
可否請您幫我看一下呢..真的蠻困擾的

在Default.aspx內有兩個TextBox及兩個Label 有兩個Button分別取出TextBox的値到Label,以及產生圖形

在Default.aspx.vb內:

Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Label1.Text = TextBox1.Text
Label2.Text = TextBox2.Text
End Sub

Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim list1 As New PointPairList()
Dim list2 As New PointPairList()
Dim list3 As New PointPairList()
Dim i As Integer, rand As New Random()
Dim y As Integer, x1 As Integer, x2 As Integer, x3 As Integer
For i = 1 To 5
y = i
x1 = 100.0 + rand.NextDouble() * 100.0
x2 = 100.0 + rand.NextDouble() * 100.0
x3 = 100.0 + rand.NextDouble() * 100.0

list1.Add(x1, y)
list2.Add(x2, y)
list3.Add(x3, y)

Next i
End Sub


在z_graph.aspx.vb內:
Protected Sub OnRenderGraph1(ByVal zgw As ZedGraphWeb, ByVal g As System.Drawing.Graphics, _
ByVal masterPane As zedgraph.MasterPane) Handles ZedGraphWeb1.RenderGraph

' Get a reference to the GraphPane instance in the ZedGraphControl
Dim myPane As GraphPane = masterPane(0)
'Dim sessionData As New Sequence


' Set the title and axis labels
myPane.Title.Text = "Stacked Bars with Value Labels Inside Each Bar"
myPane.XAxis.Title.Text = "Position Number"
myPane.YAxis.Title.Text = "Some Random Thing"

Dim list1 As New PointPairList()
Dim list2 As New PointPairList()
Dim list3 As New PointPairList()

list1 = CType(Session("list1"), PointPairList)
list2 = CType(Session("list2"), PointPairList)
list3 = CType(Session("list3"), PointPairList)

' Create the three BarItems, change the fill properties so the angle is at 90
' degrees for horizontal bars
Dim bar1 As BarItem = myPane.AddBar("Bar 1", list1, Color.Red)
bar1.Bar.Fill = New Fill(Color.Red, Color.White, Color.Red, 90)
Dim bar2 As BarItem = myPane.AddBar("Bar 2", list2, Color.Blue)
bar2.Bar.Fill = New Fill(Color.Blue, Color.White, Color.Blue, 90)
Dim bar3 As BarItem = myPane.AddBar("Bar 3", list3, Color.Green)
bar3.Bar.Fill = New Fill(Color.Green, Color.White, Color.Green, 90)

' Set BarBase to the YAxis for horizontal bars
myPane.BarSettings.Base = BarBase.Y
' Make the bars stack instead of cluster
myPane.BarSettings.Type = BarType.Stack

' Fill the axis background with a color gradient
myPane.Chart.Fill = New Fill(Color.White, _
Color.FromArgb(255, 255, 166), 45.0F)

masterPane.AxisChange(g)
End Sub

現在的問題是似乎連隨機取的值都抓不到因此跑不出圖形.我是希望能讀取label的値進而產生圖

程式碼似乎過長,不知是否會過亂,真的非常抱歉,請教你這時該怎麼辦呢
鳥毅寫道…
這位大哥:
首先要向您道歉,小弟在貼code時,因為html碼不吃角刮號,所以某些code被blogger的編輯器吃掉了:P
問題很簡單呀,您在Button2_Click那裏並沒有把list1,2,3加到Session(就是我被吃掉的code,等會就補上),所以在next i與end sub中間加上
Session("list1") = list1 等3行,就傳得到 z_graph.aspx

另一點,您在z_graph.aspx.vb裏的是OnRenderGraph1而不是OnRenderGraph,請確認aspx裏呼叫的事件名稱是否一致。
匿名表示…
大哥 非常感謝你

我已經成功的讀到label的資料了

在大哥您的最後一行有說到

遇到brower cache圖的問題是什麼意思呢?
是說遇到讀取到的圖是舊的嗎?
我有大概爬了一下文
是否是Browser快取(Cache)起來,除非主動清除這些快取資料,或人工將頁面重新整理才能讀到新的資料的意思。

希望大哥能夠為小弟稍稍講解一番
還有就是解決方法,能否向小弟講解一下,
因為不太了解C#,非常感謝你
Image1.ImageUrl = "zedgraph.aspx?xxx="+DateTime.Now.Millisecond;

xxx是固定的xxx還是某個變數呢?
希望大哥能為小弟稍稍講解,讓小弟可以順利的轉成VB
鳥毅寫道…
關於browser cache,可以google "IE6 cache problem",大致上就是你說的意思,圖不會更新。

因此,最簡單的方法就是改變url,所以我那行用VB.Net寫就是
Image1.ImageUrl = "zedgraph.aspx?xxx=" & DateTime.Now.Millisecond
其中 xxx 只要你爽寫成什麼都可以:P
匿名表示…
非常感謝你 大哥^^
最後再請教一下
Image1.ImageUrl = "zedgraph.aspx?xxx=" & DateTime.Now.Millisecond
是寫到Button1_Click內對吧

非常感謝大哥解決我的問題,因為ZedGraph的資料真的不多,除了官方網站..

感謝!! :D
鳥毅寫道…
Read the sample code again, I've updated it.
vincentwin寫道…
在zedgraph.aspx中,刪除原有內容,輸入.....
CodeFile="zedgraph.aspx.cs"
上面這一句害我不淺,我的project編譯發佈到遠端後,就只剩dll跟aspx檔,CodeFile好像是asp.net 1.1的屬性,發佈到遠端後,iis就一直找不到"zedgraph.aspx.cs",除非我手動copy過去,在vs2005中測試倒是正常得很,因為cs檔就在身邊,看一看官方範例就是寫成CodeFile,範例中竟然還寫(This file uses the newer syntax for the .Net 2.0 version.) = =。後來把CodeFile改成2.0的CodeBehind,好像就解決這個問題了,整整找了兩天,希望不要有人在遇到這問題了,不用vs2005自帶的開發網頁伺服器,叫vs2005用iis運行,好像也會遇到。
鳥毅寫道…
vincentwin,你搞錯了,CodeFile才是2.0版的寫法,1.1是叫CodeBehind。

要刮別人的鬍子前,先刮自己的鬍子。
vincentwin寫道…
好吧,確實是搞錯了。查了一下之間的不同。http://charlesbc.blogspot.com/2007/08/codefile-codebehind.html
leslie1027寫道…
請問一下大大,我要列出出每個Bar的標題
要怎麼寫?? 上面標示每個Bar的字型不知道可以改嗎?? 因為我覺得滿小的....

因為我做的是動態的Bar,我需要在每個Bar上註明數量,不過因為動態的原因,那些數字都會留下之前顯示果的數量,找了好久都不知道怎麼解決...

可以請大大幫幫忙嗎?? 謝謝喔!!
鳥毅寫道…
leslie1027,我不寫code已經半年了,最近連公司的桌機也換成Ubuntu,手邊已經沒有.Net的開發環境。

而ZedGraph我也只有稍微玩一下,請再加油或找高手協助吧!我不過是個肉腳半吊子兩光網管呀!
匿名表示…
先感謝鳥毅大大的詳細說明,
不知道原因,本人照用回大大的code卻出下以問題。

----------------------------------
編譯器錯誤訊息: CS0246: 找不到型別或命名空間名稱 'PointPairList' (您是否遺漏 using 指示詞或組件參考?)


{
行 17: PointPairList list1 = new PointPairList();
行 18: PointPairList list2 = new PointPairList();
行 19: PointPairList list3 = new PointPairList();

----------------------------------

感謝大大的解答~!!
鳥毅寫道…
匿名:
請下載文中的 sample project zip file。遺漏 using 指示詞或組件參考表示你沒有引用,完全不懂asp.net引用組件的方式;恕我直言,請從基礎開始學。

ZedGraph不適合你,請看免費的圖表元件:Microsoft Chart Controls
Unknown寫道…
請問 如果我要畫成類似excel函數中(NORMDIST)常態分配圖,請問 哪裡可以讓你選擇你要畫圖的總類??謝謝
鳥毅寫道…
俊海:你說的是 這些 圖形種類嗎?Normal Distribution應該只要描點,所以看這個就可以了。

目前ZedGraph沒什發展了,你可以考慮Microsoft Chart Controls

這個網誌中的熱門文章

自然人憑證讀卡機驅動程式

鳥毅用的是第一代的自然人憑證讀卡機,EZ100PU(後來有同事買EZmini可以讀SIM卡似乎更好),每年報稅時用一次。 本來只是要申請些政府業務,一時之間找不到光碟,沒想到在 驅動程式下載 居然看到Linux和Mac的驅動程式,剩下的就是政府單位的網頁和程式應該改版了吧!!!

在Windows Server設定L2TP over IPSec VPN

簡單地說,macOS Sierra與iOS 10發表後,大家忽然發現Apple不再支援PPTP,所以一定得設定其他的VPN型態。若不要另外裝client,用L2TP是最方便的,SSL VPN雖然好,但若沒有安裝Agent要連線到任一電腦或是非網頁服務還是挺麻煩的。