C#發現之旅第八講 ASP.NET圖形開發帶超鏈接的餅圖
袁永福 2008-5-15
系列課程說明
為了讓大家更深入的了解和使用C#,我們將開始這一系列的主題為“C#發現之旅”的技術講座??紤]到各位大多是進行WEB數據庫開發的,而所謂發現就是發現我們所不熟悉的領域,因此本系列講座內容將是C#在WEB數據庫開發以外的應用。目前規劃的主要內容是圖形開發和XML開發,并計劃編排了多個課程。在未來的C#發現之旅中,我們按照由淺入深,循序漸進的步驟,一起探索和發現C#的其他未知的領域,更深入的理解和掌握使用C#進行軟件開發,拓寬我們的視野,增強我們的軟件開發綜合能力。
本系列課程配套的演示代碼下載地址為 https://files.cnblogs.com/xdesigner/cs_discovery.zip 。
本系列課程已發布的文章有
C#發現之旅第一講 C#-XML開發
C#發現之旅第二講 C#-XSLT開發
C#發現之旅第三講 使用C#開發基于XSLT的代碼生成器
C#發現之旅第四講 Windows圖形開發入門
C#發現之旅第五講 圖形開發基礎篇
C#發現之旅第六講 C#圖形開發中級篇
C#發現之旅第七講 C#圖形開發高級篇
C#發現之旅第八講 ASP.NET圖形開發帶超鏈接的餅圖
C#發現之旅第九講 ASP.NET驗證碼技術
C#發現之旅第十講 文檔對象模型
經過以前的課程,大家都掌握了一些圖形編程技術,現在我們可以將把圖形編程技術投入到實際應用中去。在本課程中,我們一起研究使用圖形編程在ASP.NET中實現一個帶超鏈接的餅圖。
功能需求現客戶提出如下需求,
- 在ASP.NET的程序中顯示一個二維的橢圓形的餅圖界面。
- 其中每一個餅塊具有提示文本和一個超鏈接。
- 當用戶鼠標移動到某個餅塊時瀏覽器會顯示一些提示文本。
- 當用戶點擊該餅塊則鏈接到其他頁面使得頁面能顯示和該餅塊相關的詳細信息。
該軟件的用戶界面如圖所示

根據軟件功能需求,我已經開發出了這個軟件,首先演示運行一下這個軟件,給大家一個直觀的了解。
演示程序中有一個pie.aspx的頁面。該頁面就包含了一個帶超鏈接的餅圖對象,在瀏覽器中打開這個頁面,可以看到如下的用戶界面

這個頁面中顯示一個餅圖,每一個扇型都顯示數據庫中一個客戶的總訂單金額,總共顯示了10個客戶。我們鼠標移動到一個餅圖項目上,可以顯示出這個客戶的名稱和訂單金額。當鼠標點擊餅圖項目時,就會鏈接到另外一個頁面。該頁面也顯示一個餅圖,其中的項目用于顯示單個訂單的金額。鼠標放在某個項目上可以看到訂單的時間,地點,訂單人姓名和訂單金額。

在這里,第一個頁面為主頁面,第二個頁面為明細頁面。我們察看主頁面的HTML代碼,可以看到代碼為
| <img src='pieimage.aspx?name=pie_customers' usemap='#dea88afea25748bda1ae28aa3e260d1a' border='0' /> <map name="dea88afea25748bda1ae28aa3e260d1a"> <area shape="poly" coords="200,150,399,145,400,150," href="pie_orders.aspx?customerid=ALFKI" title="客戶名稱:三川實業有限公司訂單金額:518.7 點擊察看該客戶訂單的詳細情況" /> <area shape="poly" coords="200,150,397,125,399,135,399,145," href="pie_orders.aspx?customerid=ANATR" title="客戶名稱:東南實業訂單金額:2581.95 點擊察看該客戶訂單的詳細情況" /> <area shape="poly" coords="200,150,372,73,376,79,380,85,384,92,387,98,390,105,393,111,395,118,397,125," href="pie_orders.aspx?customerid=ANTON" title="客戶名稱:坦森行貿易訂單金額:7023.97 點擊察看該客戶訂單的詳細情況" /> <area shape="poly" coords="200,150,269,9,285,14,300,20,314,27,328,34,340,43,352,52,362,62,372,73," href="pie_orders.aspx?customerid=AROUT" title="客戶名稱:國頂有限公司訂單金額:12899.15 點擊察看該客戶訂單的詳細情況" /> </map> |
我們使用圖片來顯示餅圖圖形,使用map元素來在圖片上設置提示文本和超鏈接,使用多邊形來模擬扇形的超鏈接區域。明細頁面也類似。
軟件設計
文檔對象模型
餅圖是一種很常見的展示數據的方式,使用文檔對象模型的軟件設計思想來分析這個餅圖形狀,可以很容易的知道,餅圖可以抽象為餅圖對象本身和餅圖項目。
餅圖對象可以定義整體餅圖的位置和大小,它的邊界就是包含整個餅圖的橢圓的外切矩形,而且餅圖對象是個容器,可以容納若干個餅圖項目。
餅圖項目定義單個扇形區域,包括餅圖項目對應的數值,提示文本,超鏈接地址,扇形區域的起始角度和終止角度。
程序結構設計該軟件應用到ASP.NET中,在桌面程序和WEB程序中應用圖形編程具有很大的差別,WEB程序沒有視圖控件的概念,通常是采用服務器端生成包含圖形的圖片文檔來顯示圖形,由于WEB程序底層的HTTP傳輸協議是無狀態的,因此程序結構比較復雜,其結構如圖所示

在這里,采用服務器端生成包含圖形的圖片來顯示圖形。由于HTML頁面是純文本的文檔,不能包含圖片文檔等二進制數據,因此必須采用至少兩個頁面來配合顯示包含圖文的頁面,其中主頁面來顯示HTML文檔,而圖片服務頁面則專門來生成圖片文檔。
在這里說明一下程序的流程,首先客戶端瀏覽器向主頁面發出請求,該頁面中需要顯示圖形,于是生成一個圖形文檔對象,向這個圖形文檔對象填充數據,然后根據圖形文檔對象和圖片服務頁面的接口來生成專門顯示圖片的HTML代碼,同時還得講圖片文檔對象放入到一個臨時數據容器中,最常用的就是session容器。
主頁面處理完成后將生成的HTML文檔發送到客戶端瀏覽器,瀏覽器解析HTML 文檔,并根據其引用的圖片URL向服務器端的圖片服務頁面發送請求,最常見的就是IMG元素的SRC屬性。圖片服務頁面根據URL中的參數從臨時數據容器中獲得圖形文檔對象,從中獲得圖像數據,然后轉發到瀏覽器,瀏覽器獲得圖像數據后才能完整的展示主頁面生成的HTML文檔。
整個過程比較復雜,需要對HTTP和HTML有比較深的了解。
當然我們可以去掉圖片服務頁面,在主頁面中生成圖片,然后將圖片保存到一個臨時文件中,這樣做不甚合理,需要開放WEB程序訪問服務器文件系統的權限,而且圖片文件是比較大的,占磁盤空間,需要及時刪除,而如何刪除這些臨時文件也是一個問題。而采用圖片服務頁面是在內存中生成圖像文檔的,不用寫臨時文件,這樣做方便網站的維護。
軟件代碼說明根據軟件設計,使用VS.NET2003開發出這樣的程序,現對該程序代碼進行詳細說明。首先說明一下餅圖文檔對象模型。
餅圖項目對象 PieShapeItem本類型定義了餅圖中的一個項目,代碼如下
| /// <summary> /// 單個餅圖形狀項目 /// </summary> /// <remarks>該類型是PieShape列表的成員類型</remarks> [System.Serializable()] public class PieShapeItem { private double dblValue = 0 ; /// <summary> /// 數值 /// </summary> public double Value { get{ return dblValue ;} set{ dblValue = value;} } private string strText = null; /// <summary> /// 對象文本 /// </summary> public string Text { get{ return strText ;} set{ strText = value;} } private string strLink = null; /// <summary> /// 項目鏈接地址 /// </summary> public string Link { get{ return strLink ;} set{ strLink = value;} } private Color intColor = Color.Black ; /// <summary> /// 項目顏色 /// </summary> public Color Color { get{ return intColor ;} set{ intColor = value;} } /// <summary> /// 開始角度 /// </summary> internal float StartAngle = 0 ; /// <summary> /// 結束角度 /// </summary> internal float EndAngle = 0 ; }//public class PieShapeItem |
這個代碼很簡單,也就是定義了一個餅圖項目的一些屬性。
餅圖文檔對象 PieShape本對象定義了一個完整的餅圖,是本程序的核心模塊。本對象首先是PieShapeItem的一個強類型列表,它是從System.Collections.CollectionBase上面派生的,通過使用對象的Add或Remove方法就能往這個對象添加或刪除餅圖項目對象PieShapeItem。當向列表添加項目時未指定項目顏色時會創建一個默認顏色,本類型定義了一個StdColors的靜態的顏色數組,新餅圖元素的顏色就根據這個顏色數組進行分配。
對象定義了一個RefreshState方法用來計算各個餅圖元素對應的扇形區域的起始角度和終止角度。
| /// <summary> /// 刷新對象狀態 /// </summary> /// <remarks> /// 本函數中反向遍歷所有的餅圖項目, /// 計算各個餅圖項目的起始和終止角度</remarks> public void RefreshState() { double TotalValue = 0 ; foreach( PieShapeItem item in this ) { TotalValue += item.Value ; } float AngleCount = 0 ; for( int iCount = this.Count - 1 ; iCount >= 0 ; iCount -- ) { PieShapeItem item = this[ iCount ] ; float angle = ( float ) ( 360.0 * item.Value / TotalValue ) ; item.StartAngle = ( float ) Math.Round( AngleCount , 3 ) ; item.EndAngle = ( float ) Math.Round( AngleCount + angle , 3 ) ; AngleCount += angle ; item.StartAngle = ( float ) Math.Round( FixAngle( item.StartAngle ) , 3 ); item.EndAngle = ( float ) Math.Round( FixAngle( item.EndAngle ) , 3 ) ; } } /// <summary> /// 根據橢圓形狀修正角度 /// </summary> /// <param name="angle">原始角度</param> /// <returns>修正后的角度值</returns> private float FixAngle( float angle ) { if( ( angle % 90.0 ) == 0 ) return angle ; if( intWidth == intHeight ) return angle ; double x = intWidth * Math.Cos( angle * Math.PI / 180 ); double y = intHeight * Math.Sin( angle * Math.PI / 180 ); float result = ( float ) ( Math.Atan2( y , x ) * 180 / Math.PI ); if( result < 0 ) result += 360 ; return result ; } |
在這個方法中,我們首先計算所有項目的數值和。然后遍歷所有的項目,計算它的扇形區域的起始角度和終止角度。由于要顯示的是橢圓形餅圖,相對于正圓圖形是被壓扁的,因此這個起始角度和終止角度需要調用FixAngle函數進行修正。
由于在計算機屏幕上繪制扇形是順時針方向的,而我們察看一般習慣是順時針看的,因此這里需要反向遍歷餅圖項目。
計算各個餅圖項目的角度數據后,我們可以繪制圖形或者獲得圖形定位信息。首先我們定義函數CreatePath函數來獲得指定餅圖項目的路徑對象。代碼如下。
| /// <summary> /// 為一個餅圖項目創建路徑對象 /// </summary> /// <param name="item">餅圖項目</param> /// <returns>創建的路徑對象</returns> public GraphicsPath CreatePath( PieShapeItem item ) { GraphicsPath path = new GraphicsPath(); path.AddPie( intLeft , intTop , intWidth , intHeight , item.StartAngle , item.EndAngle - item.StartAngle ); return path ; } |
這個函數也很簡單,創建一個路徑對象,然后添加一個扇形區域。
我們定義了一個Draw函數來繪制餅圖圖形。其代碼為
| /// <summary> /// 繪制餅圖圖形 /// </summary> /// <param name="g">圖形繪制對象</param> /// <param name="ClipRectangle">剪切矩形</param> public void Draw( Graphics g , Rectangle ClipRectangle ) { foreach( PieShapeItem item in this ) { using( GraphicsPath path = CreatePath( item )) { using( SolidBrush b = new SolidBrush( item.Color )) { g.FillPath( b , path ); g.DrawPath( Pens.Black , path ); } } } } |
這個方法也不復雜,也就是遍歷所有的餅圖項目,為每一個項目創建路徑對象,然后使用餅圖項目的顏色填充路徑,并使用黑線繪制路徑的邊框。
由于本程序是ASP.NET程序,需要在服務器端生成圖片文檔,因此本對象也提供了CreateBitmap函數來生成包含圖形的位圖對象。其代碼為
| /// <summary> /// 創建一個包含對象圖形位圖對象 /// </summary> /// <returns>創建的位圖對象</returns> public Bitmap CreateBitmap( ) { Bitmap bmp = new Bitmap( intWidth + 1 , intHeight + 1 ) ; using( Graphics g = Graphics.FromImage( bmp )) { g.Clear( Color.White ); g.TranslateTransform( intLeft , intTop ); g.SmoothingMode = SmoothingMode.HighQuality ; Draw( g , this.Bounds ); } return bmp ; } |
這個函數里面首先根據對象大小生成一個位圖對象,然后在這個位圖對象上創建一個圖形繪制對象,填充白色背景,并進行一下坐標轉換,然后調用Draw函數繪制對象圖形。然后函數就返回創建的位圖對象了。
這個對象還定義了一個GetHtmlString的函數向主頁面提供一個顯示餅圖的HTML代碼。
| /// <summary> /// 創建用于顯示餅圖圖片和超鏈接的HTML代碼字符串 /// </summary> /// <param name="imgsrc">圖片地址</param> /// <returns>創建的HTML字符串</returns> /// <remarks> /// 此處沒有簡單拼湊HTML字符串,而是利用XML和HTML的相似性 /// 使用一個XmlTextWriter來生成HTML字符串。 /// </remarks> public string GetHtmlString( string imgsrc ) { if( this.Count == 0 ) return ""; // 生成唯一的 map 元素名稱 string name = System.Guid.NewGuid().ToString("N"); // 生成 XmlTextWriter 對象 System.IO.StringWriter myStr = new System.IO.StringWriter(); System.Xml.XmlTextWriter writer = new XmlTextWriter( myStr ); writer.IndentChar = ' ' ; writer.Indentation = 3 ; writer.Formatting = System.Xml.Formatting.Indented ; // 開始輸出HTML writer.WriteStartDocument(); // 輸出圖片元素 writer.WriteRaw("<img src='" + imgsrc + "' usemap='#" + name + "' border='0'/>"); // 輸出 map 元素 writer.WriteStartElement("map"); writer.WriteAttributeString("name" , name ); foreach( PieShapeItem item in this ) { // 輸出超鏈接區域 Point[] ps = this.GetPoints( item ); writer.WriteStartElement("area"); writer.WriteAttributeString("shape" , "poly"); writer.WriteStartAttribute("coords" , null ); for( int iCount = 0 ; iCount < ps.Length ; iCount ++ ) { writer.WriteString( ps[ iCount ].X.ToString() ); writer.WriteString("," ); writer.WriteString( ps[ iCount ].Y.ToString() ); writer.WriteString("," ); } writer.WriteEndAttribute(); if( item.Link != null && item.Link.Length > 0 ) { writer.WriteAttributeString("href" , item.Link ); } writer.WriteAttributeString("title" , item.Text ); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteEndDocument(); writer.Close(); string html = myStr.ToString(); // 修正輸出的HTML字符串 int index = html.LastIndexOf("?>"); if( index > 0 ) { html = html.Substring( index + 2 ); } return html ; } /// <summary> /// 獲得包圍餅圖區域的點坐標數組 /// </summary> /// <param name="item">餅圖項目</param> /// <returns>點坐標數組</returns> private Point[] GetPoints( PieShapeItem item ) { GraphicsPath path = CreatePath( item ); path.Flatten(); PointF[] ps = path.PathPoints ; path.Dispose(); Point[] ps2 = new Point[ ps.Length ] ; for( int iCount = 0 ; iCount < ps.Length ; iCount ++ ) { ps2[ iCount ].X = ( int ) ( ps[ iCount ].X ); ps2[ iCount ].Y = ( int ) ( ps[ iCount ].Y ); } return ps2 ; } |
這個函數參數是圖片URL地址,該地址有主頁面的程序提供,首先我們使用GUID類型來創建一個唯一的名稱,我們沒有直接使用字符串拼湊來生成HTML字符串,而是使用XmlTextWriter來書寫HTML字符串。
首先是輸出img標簽,然后輸出一個map標簽。
由于餅圖項目可以帶有超鏈接,這里就能生成map/area元素來在圖片中定義熱點,在HTML語法中,圖片熱點沒有扇形區域種類,因此只好使用使用多邊形來模擬扇形。也就是使用HTML標記”<area shape=poly coords=’多邊形的頂點坐標’ />”來模擬扇形熱點。
我們遍歷所與的餅圖項目,調用GetPoints函數獲得模擬這個項目對應的扇形區域的點坐標,將這些點坐標數據填寫到area元素的coord屬性中,然后還填上title屬性來顯示提示文本,設置href屬性來設置這個圖片熱點的超鏈接地址。
HTML文檔輸出完畢后我們獲得這個XML字符串,去掉前面的XML聲明頭,然后就把這個XML字符串當作HTML字符串返回給主程序了。
GetPoints函數就是獲得模擬扇形區域的點坐標,首先是根據餅圖項目創建一個路徑,調用路徑的Flatten函數進行線段模擬運算,然后獲得它的PathPoints屬性,這個屬性值是PointF類型的數組,需要轉換為Point數組。
主頁面 pie.aspx這個頁面是演示程序的主頁面,界面上放置一個名為lblResult的標簽,察看它的C#代碼也不復雜,主要是Page_Load方法,其代碼為
| private void Page_Load(object sender, System.EventArgs e) { // 連接數據庫 using( OleDbConnection conn = new OleDbConnection()) { conn.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + this.Server.MapPath("demomdb.mdb"); conn.Open(); // 查詢數據庫 using( OleDbCommand cmd = conn.CreateCommand()) { cmd.CommandText = @" select top 10 customers.customerid , customers.companyname as 客戶名稱, ( select sum( round( orderdetails.unitprice * orderdetails.quantity * ( 1.0 - orderdetails.discount) , 2 ) ) from orderdetails , orders where orderdetails.orderid = orders.orderid and orders.customerid = customers.customerid ) as 訂單總金額 from customers"; OleDbDataReader reader = cmd.ExecuteReader(); // 創建餅圖對象 PieShape pie = new PieShape(); pie.Width = 400 ; pie.Height = 300 ; System.IO.StringWriter writer = new System.IO.StringWriter(); while( reader.Read()) { string id = Convert.ToString( reader.GetValue( 0 )); double Value = Convert.ToDouble( reader.GetValue( 2 )); string Text = "客戶名稱:" + Convert.ToString( reader.GetValue( 1 )) + "\r\n訂單金額:" + Convert.ToString( reader.GetValue( 2 )) + "\r\n點擊察看該客戶訂單的詳細情況" ; string Link = "pie_orders.aspx?customerid=" + id ; pie.Add( Value , Text , Link ); }//while reader.Close(); pie.RefreshState(); this.Session["pie_customers"] = pie ; this.lblResult.Text = pie.GetHtmlString("pieimage.aspx?name=pie_customers"); this.DataGrid1.DataSource = pie ; this.DataGrid1.DataBind(); } } } |
這個函數中首先是連接數據庫,并執行一個SQL查詢,查詢結果是10個客戶編號,名稱及其名下所有的訂單總金額。
創建一個餅圖文檔對象,然后遍歷查詢結果,對每一條記錄都調用餅圖文檔對象的Add方法向餅圖文檔中添加一個餅圖項目。
調用餅圖文檔對象的RefreshState方法刷新餅圖的內部狀態,然后將餅圖對象保存到Session中拱將來的圖片服務頁面使用。然后設置lblResult標簽的文本為餅圖對象生成的HTML字符串。
這里還有一個DataGrid只是用來簡單的顯示餅圖文檔的內容。
圖片服務頁面 pieimage.aspx本頁面用來生成餅圖圖形的圖像文檔,該頁面沒有任何HTML代碼,其C#代碼也很簡單,只有一個Page_Load函數,其代碼為
| private void Page_Load(object sender, System.EventArgs e) { // 獲得參數 string name = this.Request.QueryString["name"] ; if( name == null ) { return ; } // 獲得餅圖對象 PieShape pie = this.Session[ name ] as PieShape ; if( pie == null ) { return ; } using( Bitmap bmp = pie.CreateBitmap()) { this.Response.ContentType = "image/png"; System.IO.MemoryStream ms = new System.IO.MemoryStream(); bmp.Save( ms , System.Drawing.Imaging.ImageFormat.Png ); ms.WriteTo( this.Response.OutputStream ); ms.Close(); } } |
該頁面試圖從session中加載頁面參數指定的名稱的餅圖文檔對象,并利用該文檔對象的CreateBmp函數獲得一個位圖對象,然后輸出PNG格式的圖像數據。
這個圖片服務頁面要正常工作,需要事先將餅圖文檔對象保存到Session中,然后使用正確的參數來調用這個頁面。
主頁面 pie_customers.aspx中,已經將生成的文檔對象使用名稱pie_customers保存到Session中,同時它生成的HTML代碼中使用的圖片地址就是 pieimage.aspx?name=pie_custoemrs ,主頁面為了圖片服務頁面正常工作已經做好了充分的準備。由于主頁面和圖片服務頁面密切配合工作,客戶端瀏覽器中才能完整的顯示餅圖圖形。
在這里使用了位圖對象的Save函數來輸出圖片文檔的二進制數據。這里沒有直接輸出到頁面的輸出流中,因為那樣做是會報錯的,這里創建了一個臨時的內存流對象,將圖片數據輸出到這個內存流中,然后將這個內存流中的數據輸出到頁面輸出流中。
二級主頁面 pie_orders.aspx在主頁面pie_custoemrs.aspx中新增餅圖元素時還設置了餅圖項目的超鏈接“pie_orders.aspx?customerid=客戶編號”,這樣用戶點擊餅圖圖片中的熱點時就能跳轉到二級主頁面來顯示指定客戶的訂單詳細信息。
二級主頁面和主頁面有點類似,都用一個餅圖來顯示數據。其頁面結構和處理過程也差不多,它的Page_Load方法代碼為
| private void Page_Load(object sender, System.EventArgs e) { string customerid = this.Request.QueryString["customerid"] ; if( customerid == null || customerid.Length == 0 ) return ; // 連接數據庫 using( OleDbConnection conn = new OleDbConnection()) { conn.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + this.Server.MapPath("demomdb.mdb"); conn.Open(); // 查詢數據庫 using( OleDbCommand cmd = conn.CreateCommand()) { cmd.CommandText = @" SELECT OrderDate AS 訂購時間, shipname AS 運輸人, shipaddress AS 地點, ( select sum( round( unitprice * quantity * ( 1 - discount) , 3 ) ) from orderdetails where orderdetails.orderid = orders.orderid ) AS 總金額 FROM orders WHERE customerid ='" + customerid + "'" ; OleDbDataReader reader = cmd.ExecuteReader(); // 創建餅圖對象 PieShape pie = new PieShape(); pie.Width = 400 ; pie.Height = 300 ; System.IO.StringWriter writer = new System.IO.StringWriter(); while( reader.Read()) { double Value = Convert.ToDouble( reader.GetValue( 3 )); string Text = "時間:" + reader.GetValue( 0 ) + "\r\n人員:" + reader.GetValue( 1 ) + "\r\n地點:" + reader.GetValue( 2 ) + "\r\n金額:" + reader.GetValue( 3 ); string Link = "#" ; pie.Add( Value , Text , Link ); }//while reader.Close(); // 刷新餅圖狀態 pie.RefreshState(); this.Session["customerid"] = pie ; this.lblResult.Text = pie.GetHtmlString("pieimage.aspx?name=customerid"); this.DataGrid1.DataSource = pie ; this.DataGrid1.DataBind(); }//using }//using } |
本頁面中,首先從頁面參數中獲得客戶編號,然后連接數據庫,進行SQL查詢,獲得指定客戶編號的所有訂單記錄。然后將查詢所得數據填充到一個餅圖文檔對象,然后將該文檔對象使用名稱customerid保存到Session中,這樣未來運行的圖片服務頁面就從Session中獲得名稱為cusotmerid的餅圖文檔對象來顯示餅圖圖片了。
小結在本次課程中,我們研究在在ASP.NET中使用圖形編程來顯示一個帶超鏈接的餅圖圖形,可以看出在ASP.NET中由于WEB程序的機構特點,其圖形編程比在桌面開發中要復雜,涉及了很多知識,其實是HTML和圖形編程的混合。
posted on 2008-06-04 09:12 袁永福 電子病歷,醫療信息化 閱讀(7069) 評論(17) 收藏 舉報
浙公網安備 33010602011771號