顯示具有 Programming 標籤的文章。 顯示所有文章
顯示具有 Programming 標籤的文章。 顯示所有文章

星期一, 十月 06, 2008

你的程式N-Tier了嗎?

鳥毅退伍後,找的第一間公司是負責銀行端末系統的公司,有點類似獨孤木以前做的,但規模比較小。當時年少無知,以為企業內部的程式都應該長得像那樣,分層控制流程,也就是N-Tier設計。

由於有位朋友待在國內某間頗具規模的入口網站,當時向鳥毅招手,半年後鳥毅就投身到portal混了年餘,與N-Tier說拜拜,致力於高流量下還能動的web開發(因為寫得爛,能動就不錯了)。此時最大的好處,就是身邊的同事都是高手(我最爛),而且熟FreeBSD/Linux的人很多,不像現在有問題都找不到人問。

由於coding程度太差,又不耐操,後來受不了跑到目前的公司當個小網管。這間公司都用微軟的Solution,所以當然傾向於用Visual Studio拖拉。微軟的IDE特點就是快,不但跑起來快,開發時程還真是短;用VS2005/2008配合SQLServer,拉一拉一個MIS程式就做出來,這是以前沒接觸過FoxPro/PowerBuilder的鳥毅未知的世界。

此時問題就來了:一間100人以上的公司,不可能只有一個MIS寫程式。每個人寫程式都直接存取Database對嗎?其實這間傳產公司的IT主管也不是IT出身,在鳥毅來之前連套Database都沒有,後來才買套SQLServer 2000供MIS使用。到美麗的MIS小姐加入本公司後,她和原先MIS先生的風格迥異,兩個人的code也沒有共用(原因別問我),而且都是直接存取Database,造成兩個人的程式功能大幅重複,而且時常為了資料交換而有爭執。Client/Server更大的問題在,不過這不在討論之列。

要問我是誰的錯嗎?嗯....是微軟的錯:P VisualStudio一開始的設計就是Quick & Dirty,到現在Enterprise Library的架構都一直在變,也沒有類似JavaEE的Solution(應該沒有吧...我不熟:P,只有找到);所以造成MIS必須自己設計N-Tier的架構。(這一段是寫給MIS看的,微軟別怪我

微軟的N-Tier似乎和LINQ畫上等號(),雖然在下不怎麼認同,但至少是個好的開始。現在又多了ASP.Net MVC,不知未來如何?

總之,若你是公司的IT主管,請注意元件的共用與源碼的共用,N-Tier是建構在Business Logic上而不是資料流上,不要用Database去思考程式。

唉,我這個小小網管似乎想太多了,優秀的年輕同事聽到交大把email外包給gmail就想到長官一定會擔心被人說太閒,不懂長官的心,難怪我會黑呀!

星期一, 九月 22, 2008

OLEDB比ODBC快呀!

看了李維回覆RURU有關ODBC, OLEDB, ADO和dbExpress的問題,想起VB超人告訴過我OLEDB比較快,因為ODBC多了一層呼叫。

雖然李維很強(是高手中的高手呀),但他對微軟資料庫的歷史似乎不是完全正確,記得微軟最早提出RDO,後來才有ADO的出現,而ADO裏再分OLEDB與ODBC。

以前有別人測過,但以讀取為主。(請看

所以在下敝人小弟我,用VB6寫了一個很破的測試程式,為避免cache,就只寫不讀取,完全避開Server的Cache機制,不然比較晚讀取的連線一定比較快呀!

測試方法簡述如下:兩個程式架構完全相同,對同一個DB寫入十萬次,執行前先truncate table。

兩次測試結果相同:OLEDB花了1分16秒,ODBC花了1分21秒。

結論:其他Database不敢說,但在MS SQLServer的情況下,OLEDB比ODBC快!

Update:一樣的程式,原測試環境在Win2000 VM,剛才心血來潮在Vista 64上執行,居然差不多。微軟在不同OS上可能有許多不同的tuning與cache;所以對於這種非程式技巧的效能,還是別研究了:P

原始碼:
Private Sub OleDBTest()
Dim conn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim sql As String
Dim before As Date
Dim after As Date
Dim duration As Date
Dim connstr As String
connstr = "Provider=SQLOLEDB;" & _
"Data Source=MySQLServer;" & _
"Initial Catalog=TestDB;" & _
"User ID=sa;" & _
"Password=sa;"
before = DateTime.Now
Set conn = New ADODB.Connection
conn.Open (connstr)
Dim i As Long
For i = 1 To 100000
sql = "INSERT INTO TestTable Values('" & CStr(i) & "')"
conn.Execute (sql)
Next i
after = DateTime.Now

conn.Close
Set conn = Nothing
duration = after - before
Label1.Caption = ""
Label1.Caption = "before:" & CStr(before) & _
vbCrLf & _
"after:" & CStr(after) & _
vbCrLf & _
"duration:" & CStr(duration)
End Sub

Private Sub ODBCTest()
Dim conn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim sql As String
Dim before As Date
Dim after As Date
Dim duration As Date
Dim connstr As String

connstr = "Driver={SQL Server};" & _
"Server=MySQLServer;" & _
"Database=TestDB;" & _
"UID=sa;" & _
"PWD=sa;"

before = DateTime.Now
Set conn = New ADODB.Connection
conn.Open (connstr)
Dim i As Long
For i = 1 To 100000
sql = "INSERT INTO TestTable Values('" & CStr(i) & "')"
conn.Execute (sql)
Next i
after = DateTime.Now

conn.Close
Set conn = Nothing
duration = after - before
Label1.Caption = ""
Label1.Caption = "before:" & CStr(before) & _
vbCrLf & _
"after:" & CStr(after) & _
vbCrLf & _
"duration:" & CStr(duration)
End Sub


最後Update:VB超人最近接某個案子剛好在Oracle 11g轉檔,他表示Oracle odbc 插入一萬筆要51秒,而OleDB只要14秒,用Oracle donet client 只要八秒,在XP下作業。請看VB超人提供的測試結果圖:(測試案例稍有不同)

星期二, 九月 16, 2008

今日連結:追求神乎其技的程式設計之道

最近頹廢太久,今天才發現高人vgod要去MIT攻讀博士學位,他臨走前寫了幾篇神文,特此記錄之,以便日後查閱拜讀。(算一算vgod的年紀,應該和jserv老大是台中一中前後屆吧?果然是名校呀!)

追求神乎其技的程式設計之道(一)
追求神乎其技的程式設計之道(二)
追求神乎其技的程式設計之道(三)
追求神乎其技的程式設計之道(四)
追求神乎其技的程式設計之道(五)
追求神乎其技的程式設計之道(六)

星期一, 九月 08, 2008

駭客中繼站阻絕設定

VB超人接了某個政府機關的案子,有台主機代管在敝公司。日前很不幸地又收到資安管理通知,這次是要求做駭客中繼站阻絕設定,設定方法如下:
DNS設定步驟
  1. 建立內部DNS(如:192.168.0.2),並且要求所有內部電腦必須透過本DNS方可查詢。
  2. 新增一組Domain(如:blogdns.com) ,令此網域之查詢均指向一組虛設IP(如:1.1.1.1)。不論查詢 *. blogdns.com等,均回覆前述虛設IP。
  3. 於防火牆設定一組阻擋該虛設IP的規則(如:reject all dst ip=1.1.1.1)。
  4. 重複步驟2至3,將所附之DN設定完畢。
敝公司的DNS就是小弟在管理,因為合約關係,我不得不幫VB超人搞定這個需求。目前有348個host,若一一設定不累死我?

首先簡化問題,不管是不是同個domain,把每個host都放在一個zone總可以吧?結果一定相同。因此,就寫個簡單的script將host列表轉成dns file;由於使用Windows DNS,所以可以把dns file放在%windir%\system32\dns,再利用registry匯入即可。
Windows DNS Server的機碼在[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DNS Server\Zones]底下。
# coding: utf-8

def WriteDNSFile(list, adserver):
for domain in list:
domain = domain.strip();
f=open(domain+'.dns', 'w+');
f.write(";\n; Database file "+domain+".dns for "+domain+" zone.\n");
f.write("; Zone version: 1\n;\n\n");
f.write("@\tIN SOA "+adserver+". hostmaster."+adserver+". (\n");
f.write("\t\t1\t; serial number\n");
f.write("\t\t900\t; refresh\n");
f.write("\t\t600\t; retry\n");
f.write("\t\t86400\t; expire\n");
f.write("\t\t3600\t) ; default TTL\n");
f.write(";\n; Zone NS records\n;\n\n");
f.write("@\t\tNS\tyouradserver.corp.\n\n");
f.write(";\n; Zone records\n;\n\n");
f.write("@\t\t A\t1.1.1.1\n\n");
f.close();

def WriteDNSRegFile(list):
f=open("dns.reg", "w+");
f.write("Windows Registry Editor Version 5.00\n\n");
for domain in list:
domain = domain.strip();
f.write("[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\DNS Server\\Zones\\"+domain+"]\n");
f.write('"Type"=dword:00000001\n');
f.write('"SecureSecondaries"=dword:00000003\n');
f.write('"NotifyLevel"=dword:00000002\n');
f.write('"DatabaseFile"="'+domain+'.dns"\n');
f.write('"NoRefreshInterval"=dword:000000a8\n');
f.write('"RefreshInterval"=dword:000000a8\n');
f.write('"Aging"=dword:00000000\n\n');
f.close();

#主程式
adserver="youradserver.corp" #AD及DNS Server, 這裏是youradserver.corp
hostListFile = ur"歷次中繼站列表匯整檔.txt";
f=open(hostListFile, 'r');
list = f.readlines();
f.close();
WriteDNSRegFile(list);
WriteDNSFile(list, adserver);

本程式提供給有此需求的朋友,版權沒有,盡管盜貼。

星期五, 九月 05, 2008

將所需組件包在一個exe裏

Blogger終於修好了,因此從今天開始再寫技術文章。

美麗的MIS小姐發現MIS先生安裝在使用者端的程式只有一支exe檔,而且其中還有Crystal Reports XI的Runtime。由於種種原因,她問鳥毅這是怎麼做的。

鳥毅只知道Java的Jar檔是zip,所以很容易把多個jar壓成一個;傳統C/C++可以用static link;遇到.Net就沒轍了,連忙請教google。

原來得靠ILMerge這個工具,ILMerge下載點

用指令比較麻煩,可以下載Gilma 這個UI介面,也不用記住一堆東西。


ILMerge不是萬靈丹,也是有點問題,別忘了看ILMerge.doc裏的第4點Troubleshooting。

Update:感謝Kuon的告知,DILMerge在.Net 1.1/2.0均可使用,不像ILMerge只能在.Net 2.0用,而且DILMerge還有提供Delphi的Pascal原始碼。(我猜用free pascal應該編不過吧...)

星期一, 八月 18, 2008

JavaScript的Ascii2Native

為了個人需求,需要用JavaScript做unicode escape轉換。

對岸的高手在 Native2Ascii的JavaScript实现 (便于平时使用) 有,但他寫的ascii2native明顯有Bug。

不才在下修改成:
function ascii2native() {
a = document.getElementById('ascii').value;
n = unescape(a.replace(/\\u/ig, "%u"));
document.getElementById('native').value = n;
}

星期二, 七月 29, 2008

避免系統被super programmer獨霸

因為有位"An Embedded System Engineer"問要如何解決"系統被super programmer獨霸",鳥毅在留言已回答要採用Pair Programming。

但若照本宣科實行上一定會遇到困難,就鳥毅所認識的朋友裏,沒有一間資訊公司採用Pair Programming。所以在此推薦一位高手:ingramchen。他在這篇提到採用Pair Programming,另外幾篇請看此搜尋結果

若對Pair Programming沒啥概念,可以先參考Q大寫的 程式碼共同擁有,有助能力提升

星期一, 七月 28, 2008

重構不是萬靈丹

若是公司資訊主管,又沒有公用的源碼 repository,請你就別往下看了。

上星期看到Q大 程式碼共有下的團隊開發 馬上就轉寄給主管看。

其實Q大寫得很客氣,一間公司的系統沒有明確的code conventions,又不推動code share,只是讓這個系統綁死在某個programmer手上。

就算這個高手再強,品德再好,他總有一天會因為業務關係而不得不把舊系統交由他人維護。拿敝公司來舉例,VB超人和鳥毅交情好,他寫的程式也很簡捷,但是風格差異實在過大。當鳥毅接收VB超人所開發的瀏覽程式時,根本無從下手。這兩年有需要修改的部份,仍然要請VB超人撥冗修改。就算鳥毅有心想要花時間研究,但來自使用者的壓力與其他業務根本不允許花幾天去看別人的程式。

此時或許會有朋友說:重構就好了嘛!在VB6沒有重構工具的情況下,要不影響原程式重構實在很困難;就算能重構,光修改變數和物件,就不知道要花幾天時間。

侯捷在重構的譯序裏提到:
我比較擔心,閱歷不足的程序員在讀過本書後可能發酵出「先動手再說,死活可重構」的心態,輕忽了事前優秀設計的重要性。任何技術上的說法都必須有基本假設;雖然重構(或更向說 XP,eXtreme Programming)的精神的確是「不妨先動手」,但若草率行事,代價還是很高的。重型開發和輕型開發各有所長,各有應用,世間並無萬應靈藥,任何東西都不能極端。過猶不及,皆不可取!
事實就是如此,重構不是萬靈丹,好的開發方法與演算法遠勝過程式技巧。事前的需求分析與架構的設計,在一開始就決定軟體的未來。(這幾年已漸漸沒在寫code,所以看到 重構-向範式前進 雖然很想買,但看了也用不到,還是作罷。)

XP和CMMI不同,並不是空泛的教戰守則,而是大師的經驗法則。勿以善小而不為,用在XP上亦然呀!拿一間已被收購的入口網站舉例:這間公司在香蕉國拿下超高流量,但是裏頭RD各寫各的程式,雖然每個人的code都有交出來,程式技術也夠,但是卻因為沒有好的code conventions,舊服務大改版時幾乎都得重寫,服務移交給新人維護時也有許多困難,常常得找原開發者。我想這在許多中小企業也是一樣吧?只是MIS流動率較低,通常還找得到原開發者。

星期六, 七月 19, 2008

Asp.Net的TreeView Checked事件

美麗的MIS小姐希望在asp.net的TreeView點選節點時,會自動選取子節點,但TreeView為了不讓畫面一直閃動,所以沒有支援Node裏CheckBox的AutoPostBack。 這種時候,身為閒人的鳥毅當然要幫事務繁忙的MIS小姐找solution,對岸的高手早利用JavaScript與asp.net的Attribute做出來,參考:asp.net TreeView 一些操作

簡單描述一下:在aspx加上

<script language="javascript" type="text/javascript">
function TreeNodeChecked() {
var ele = window.event.srcElement;
if(ele.type=='checkbox')
{
var childrenDivID = ele.id.replace('CheckBox','Nodes');
var div = document.getElementById(childrenDivID);
if(div==null)return;
var checkBoxs = div.getElementsByTagName('INPUT');
for(var i=0;i<checkBoxs.length;i++)
{
if(checkBoxs[i].type=='checkbox')
checkBoxs[i].checked=ele.checked;
}
}
}
}
</script>
在aspx.cs的Page_Load修改為

protected void Page_Load(object sender, EventArgs e)
{
TreeView1.Attributes.Add("onclick", "TreeNodeChecked();");
}


最後,為拉抬人氣,附上美麗的MIS小姐倩影:

星期一, 七月 14, 2008

VB 6 ActiveX 在IE7的Modal Dialog失效問題

最近遇到ActiveX Modal Dialogs do not work in IE7相同的問題,解決方法在FIX: You can still interact with Internet Explorer 7 when a Visual Basic 6.0 modal form is displayed from an ActiveX control in Internet Explorer 7

以下翻譯成人話。

問題描述:IE7切換到ActiveX Modal視窗時,會"卡住"無法切換;IE6無此問題。
解決方法:安裝6.0.98.02版的Msvbvm60.dll。

原本安裝到XP SP3應該都會順便更新Msvbvm60.dll,但是好死不死,這次User使用的程式元件有用到額外的ocx,因此下載cab包裝時,把新的Msvbvm60.dll換回舊版,才發生此現象。

以上供提供給和鳥毅一樣悲情還在維護VB6 ActiveX的朋友。

星期一, 六月 23, 2008

參加微軟創新中心軟體工程卓越計劃

微軟創新中心技術論壇軟體工程卓越計劃其實非常不錯,找來在西雅圖的華人工程師。
我只參加軟體安全開發週期這場,內容雖然大半都已知道,但對於微軟開發及測試的流程有些瞭解。
唯一不滿就是微軟創新中心有點偏僻呀,到達的時候一樓還在施工:









星期三, 五月 28, 2008

Google提供AJAX Libraries

AJAX Libraries API裏提到,Google提供以下幾種AJAX Libraries:
<script src="http://www.google.com/jsapi"></script>
<script language="javascript" type="text/javascript" >
// Load jQuery
google.load("jquery", "1.2.6"); // 或 google.load("jquery", "1.2.3");
</script>

Google的水管粗,加上可以自由指定版本,實在太方便了。

星期二, 五月 20, 2008

程序員的特質?

剛看到qing推的《Top 10 Traits of a Rockstar Software Engineer》:明星程式設計師必備的十項特質丟給帥學弟,結果回丟來What Great .NET Developers Ought To Know (More .NET Interview Questions)
看完後,決定還是當個小網管吧:P

星期四, 四月 24, 2008

改用VS2008時找不到Crystal Reports的dll

Crystal Reports元件一直是個很麻煩的東西,鳥毅下載VS2008的試用版VM編譯原有的專案,在不升級元件至.Net 3.5的情況下,編譯時會顯示找不到Crystal Reports 10.2.3600版的幾個dll。這是因為Visual Studio 2008所用的Crystal Reports也跟著升級了,而且並沒有提供前版的dll。

解決方法有三:
  1. 裝一套Crystal Reports:這要花錢買耶,就算是試用版,也太麻煩了。
  2. 裝Visual Studio 2005:更累人,我又不是笨蛋。
  3. 從裝好Visual Studio 2005的機器,到%WinDir%\assembly目錄(通常是C:\Windows\assembly)底下找CrystalDecisions.Enterprise.Framework等關鍵字,就會找到這幾個dll;有些在assembly\GAC,有些在assembly\GAC_MSIL,自己照名字找,再把這幾個dll放到專案的bin目錄下即可(WinForm專案要加入參考)。
問我為什麼不升級?有二種情況:第一種情況是整個team裏只有你裝VS2008,另一種情況則是因為還在用Windows 2000 Server,無法升級到.Net 3.5(不爽的話請找微軟)。

星期五, 四月 18, 2008

OpenWebMail的附加檔檢視問題

最近同仁反應,某個人從Yahoo Webmail寄來的信在OpenWebMail開啟時會看不到附加檔。

打開這封mail後發現是因為Yahoo加了段廣告,裏頭的table設定align=left,而OpenWebMail設定附件以table align="center"顯示;所以附件被擠到右邊,在Firefox看得到,在IE看不見這個table(用IE Developer Toolbar看得到它的存在)。

鳥毅實在是命苦,這個找leeym大概也不會理我,只好自己動手惡搞,改 openwebmail/modules/htmlrender.pl ,第241列後插入一列:
$html =~ s#\<table align=left border=0 cellspacing=0#<table align=center border=0 cellspacing=0#is;

星期四, 四月 10, 2008

利用iTextSharp替PDF加上浮水印

前陣子敝公司的內部系統改版,原本用tiff格式的文件改為pdf格式,現在要加上浮水印時,發現ImageMagick做得並不好,在Convert時會讓解析度變差,因此找了一些方法後,決定用iTextSharp處理。
iTextSharp是Java平台iText的C#版,當然也可以用VB.Net呼叫。
如果對Acrobat的事件很熟悉,也可以選擇用VB 6呼叫Acrobat SDK,參考iTextSharp的效率很好,比起ImageMagick速度快、佔用的記憶體也少。
先小小抱怨一下,iTextSharp的線上教學文件很差,現在都到4.1.0版,文件還在3.1版。由於4.0版以後物件模型大改(向微軟學的?),Watermark物件已經不復存在,我也懶得去trace source code;所以拿3.1.8版來用,直接照範例寫一個簡單的class就搞定。
iTextSharp的License是LGPLMPL,不過底下的Code是GPL 3.0 XD
這很簡單,只有三個參數:原始pdf路徑名、新pdf路徑名、圖檔路徑名。圖檔要自己先做小一點,我沒有寫縮圖功能,但有加上置中。如果有人改得好用,請寄回給我:P
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace PDFWatermark
{
public class PDFWatermark {
public static void Watermark(string inputfilepath, string outputfilepath, string watermarkimagepath) {
try {
PdfReader pdfReader = new PdfReader(inputfilepath);
int numberOfPages = pdfReader.NumberOfPages;
Rectangle psize = pdfReader.GetPageSize(1);
float width = psize.Width;
float height = psize.Height;
FileStream outputStream = new FileStream(outputfilepath, FileMode.Create);
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
PdfContentByte waterMarkContent;
Image image = Image.GetInstance(watermarkimagepath);
image.SetAbsolutePosition((width - image.Width) / 2, (height-image.Height)/2);
for (int i = 1; i <= numberOfPages; i++) {
waterMarkContent = pdfStamper.GetUnderContent(i);
waterMarkContent.AddImage(image);
}
pdfStamper.Close();
pdfReader.Close();
}
catch (Exception ) {}
}
}
}

星期三, 四月 02, 2008

不是Visual Studio害JBuilder,而是Eclipse

和公司的副理在聊天時,MIS先生提到JBuilder都不太行了,他認為是Visual Studio太強。
鳥毅倒有不同的看法:不是Visual Studio害JBuilder,而是Eclipse和NetBeans功能太強,滿足大部份開發者的需求。

在許多大企業仍舊用Java開發內部系統而非.Net,只是Eclipse的出現戲劇性的改變IDE的生,演變成大企業也不一定需要買新版的JBuilder,此時Borland就難以為續。

星期三, 三月 26, 2008

動態改變RDLC報表DataSource

先用設定好的DataSet(這裏叫Report.xsd)設定RDLC檔,以Report.rdlc為例。
接者在Asp.Net報表裏拉入Report Viewer,指向Report.rdlc,刪掉datasource那些用不到的東西,在aspx裏會看到類似
<rsweb:reportviewer id="ReportViewer1" runat="server" font-names="Verdana" font-size="8pt" height="400px" width="800px">
<LocalReport ReportPath="Report.rdlc">
</LocalReport>
</rsweb:reportviewer>

RDLC檔其實是一個xml,和Crystal Reports的rpt二進位格式大不同。用文字編輯器打開來看,會看到類似:<DataSet Name="Report_ReportList"> 或是<DataSet Name>Report_ReportList</DataSet Name> 這裏Report是我在.xsd檔裏定義的DataSet名稱,而ReportList是table名稱,知道這個DataSet Name後,就可以動態改變RDLC的DataSource。

用我寫的程式片斷舉例:
private void RefreshReport()
{
ReportViewer1.LocalReport.DataSources.Clear();
DataTable dt = getReportDataTable(); //這是我寫的private method
ReportViewer1.LocalReport.DataSources.Add(
new Microsoft.Reporting.WebForms.ReportDataSource("Report_ReportList", dt));
}
很簡單吧!只要加入一個ReportDataSource,參數裏設定DataSet為相同名稱,再把DataTable傳入即可。要重新改變報表內容時,必須先執行
ReportViewer1.LocalReport.DataSources.Clear(); 
否則每次都只看到第一次傳入的內容。

星期一, 三月 24, 2008

DataTable Select的陷阱

今天在測試ZedGraph時,發現我的報表資料有錯,查了DataTable裏的內容並沒有問題,仔細檢查才知道出在 dataTable.Select("部門編號=12"); 這段。

原因很簡單,我寫的DataTable並沒有強制設定欄位的Type,所以只能把每個欄位都當做字串處理,要改成 dataTable.Select("部門編號='12'"); 就可以,特此誌之。

用ZedGraph畫統計圖

才剛貼完上一篇,馬上就有位朋友丟過來一個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 );
}
}

打開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;
這樣應該就沒問題了。