繪 圖 1 6 - 1 繪圖的基本觀念 1 6 - 3 繪圖相關類別 1 6 - 2 繪圖屬性與方法 16-4 實例研究
1 6 - 1 繪圖的基本觀念 GDI+ 相信很多人都有使用 PhotoImpact 、PhotoShop 或 Windows 小畫家的 經驗, 您要繪製直線、圓、扇形、多邊形、或輸入文字等, 都是線上操作完成, 這些操作的背後都由許多函式堆積而成。在.NET Framework 中, 則引入了 GDI 的新一代技術, 稱為 GDI+, 用來繪製圖形。 首先先了解什麼是 GDI 呢?GDI 是從 Windows 95 到 Windows 2000 隨附的舊版繪圖裝置介面(Graphics Device Interface), 是屬於繪圖方面的 API(Application Programming Interface)。因為應用程式不能直接控制硬體, 所以當我們要進行繪圖的動作時, 必須透過 GDI 才能完成。
那 GDI+ 又是什麼呢?GDI+ 是 GDI 的後續產品, 是一種繪圖裝置介 面, 可將應用程式和繪圖硬體分隔, 讓我們能夠撰寫與裝置無關的應用程式。它可以讓我們不需注意特定顯示裝置的詳細資料, 便可在螢幕或印表機顯示資訊。我們可以呼叫 GDI+ 類別所提供的方法, 然後這些方法會適當地呼叫特定的裝置驅動程式, 而完成繪圖。下表列出了一些實作 GDI+的命名空間, 這些命名空間包含許多基本與進階的繪圖類別, 供程式開發者來完成各種繪圖功能。本章在此僅介紹 System.Drawing 命名空間中一些常用的繪圖類別, 希望藉由本章的介紹, 讓讀者可以輕鬆地學習 C# 的繪圖功能。
座標系統 在我們開始繪圖之前, 一定要先知道 GDI+ 的座標系統如何定義。 GDI+ 的座標原點 (0, 0) 定義在繪圖元件的左上角, X 軸及 Y 軸分別向右及向下增加, 預設的單位長度是像素 (pixel), 如下圖所示: Graphics 類別 Graphics 類別是 GDI+的核心, 若要繪製任何圖形, 都需要先取得 Graphics物件, 設定它的屬性, 呼叫它的方法來完成繪圖的工作。由於 Graphics 類別並未公開其建構子, 故無法以建構子來建立一個 Graphics 物件, 而是要從您所要繪圖的元件取得一個 Graphics 物件, 其語法如下:
Pen 類別 以上敘述中的物件可為表單 (Form) 及 Control 類別的衍生類別 (例如, Label 、PictureBox 及 TextBox 等...), 當上述物件呼叫 CreateGraphics 方法後, 會傳回一個 Graphics 物件, 您可利用此一 Graphics 物件在建立它的物件上 繪圖。例如以下敘述可取得表單的 Graphics 物件。 Pen 類別 C# 的繪圖至少必須藉助 Graphics 與 Pen 類別物件的協助, 其中 Graphics 物件就好比一塊畫布, 而 Pen 類別物件就是畫筆了。例如, 以下敘述可 產生畫筆物件, 畫筆的線條顏色為黑色, 線條粗細為 3 。
繪圖方法 Graphics 類別的常用繪圖方法有 DrawLine(直線)、DrawRectangle(矩 形)、DrawEllipse(橢圓)、DrawCurve(曲線)、DarwArc(弧線)、DrawPie(扇形)、DrawLines(多邊形)、DrawPolygon(封閉多邊形)以及 DrawBezier(貝茲曲線)等。這些方法的詳細說明及範例請看 16-2 節。 現在先簡單的介紹一個繪製直線的方法。例如, 以下敘述可繪製一條 起點為(10, 10), 終點為(300, 100)的直線。
Paint 事件 除了可利用上述 CreateGraphics 方法取得繪圖物件來繪製圖形外, 亦可利用物件的 Paint 事件來繪製圖形, Paint 事件的語法如下: Paint 事件來自 Control 類別, 所以 Control 類別的衍生類別皆可改寫此事件, 我們可利用上述語法中 PaintEventArgs 類別的物件 e 來取得繪圖物件繪製圖形。例如, 以下敘述可利用 Paint 事件中的參數 e 繪製一條直線。
其次, Paint 事件的執行時機如下: 1. 新建的視窗。 2. 視窗從隱藏還原為可視。 3. 視窗被改變大小。 4. 視窗被別的視窗遮蓋再移開。
1 6 - 2 繪圖屬性與方法 本節將介紹一些 Graphics 類別的常用繪圖屬性與方法, 例如設定座標 單位 (PageUnit)、繪製文字 (DrawString)、直線 (DrawLine)、矩形 (DrawRectangle)、橢圓 (DrawEllipse)、弧線 (DrawArc) 等方法, 如下圖, 請看以下說明。
繪圖屬性 PageUnit 取得或設定此 Graphics 物件的座標單位, 其中座標單位必須是 GraphicsUnit 列舉型別的成員, 如下表所示。
例如, 以下敘述可將座標單位設為公釐 (mm) , 請讀者自行使用尺量線條的長度。
繪圖方法 DrawLine 繪製連接兩點的直線, 共有 4 種多載, 以下僅介紹最常用的一種多載, 其語法如下: 其中 pen 為畫筆物件, (x1, y1) 為起點的座標, (x2, y2) 為終點的座標, 如下圖所示。
例如, 以下敘述可於表單上繪製一起點為 (10, 10), 終點為 (100, 100) 的直線。 其結果如右圖:
DrawRectangle 繪製矩形, 其語法如下: 其中座標 (x, y) 為矩形左上角點, width 為矩形的寬度, height 為矩形的 高度, 如下圖所示。
例如, 以下敘述可於表單上繪出一個左上角位於 (50, 30), 寬度 200, 高度 100 的矩形。 其結果如右圖:
DrawRectangle 繪製矩形, 其語法如下: 其中座標 (x, y) 為矩形左上角點, width 為矩形的寬度, height 為矩形的 高度, 如下圖所示。
例如, 以下敘述可於表單上繪出一個左上角位於 (50, 30), 寬度 200, 高度 100 的矩形。 其結果如右圖:
DrawEllipse 繪製橢圓, 其語法如下: 在指定左上角座標 (x, y) 寬度 (width) 及高度 (height) 的矩形內繪製橢圓形, 如下圖所示。
例如, 以下敘述可於表單上, 在一個左上角位於 (50, 30), 寬度為 200,高度為 100 的矩形內繪出橢圓。 其結果如右圖
此外, 若矩形的寬度和高度相等則可繪出一正圓形。例如, 以下敘述 可於表單上, 在一個左上角位於 (50, 30), 寬度和高度皆為 100 的矩形內繪出一正圓形。 其結果如右圖:
Draw Arc 繪製弧線, 其語法如下: 在一個指定左上角座標、寬度和高度的矩形內繪製弧線, 此弧線為上述矩形所定義之橢圓的一部分。以上語法中各項參數的意義如下:
例如, 以下敘述將於一個左上角位於 (50, 50), 寬度為 100, 高度為 50的矩形內, 繪出一起始角為 90 度, 弧度角為 90 度的弧線。 其結果如右圖, 為了方便讀者觀察起始角與弧度角, 筆者亦將對應的矩形繪出。
DrawPie 繪製扇形, 其語法如下: 在一個指定左上角座標、寬度和高度的矩形內繪製扇形, 此扇形面積 為上述矩形所定義之橢圓形面積的一部分。以上語法中各項參數的意義 如下:
例如, 以下敘述將於一個左上角位於 (50, 50), 寬度為 100, 高度為 50 的矩形內, 繪出一起始角為 0 度, 弧度角為 90 度的扇形。 其結果如下圖, 為了方便讀者觀察起始角與弧度角, 筆者亦將對應的矩形繪出。
DrawLines 繪製連續線段, 其語法如下: 繪製連結 Point 結構陣列的連績線段。例如, 以下敘述可繪出一條起點為 (100, 10), 終點為 (200, 110), 並通過 (120, 70) 及 (160, 30) 兩點的連續線段。 其結果如下圖:
DrawPolygon 繪製封閉多邊形, 其語法如下: 繪製連結指定 Point 結構陣列的封閉多邊形。例如, 以下敘述可繪出 一個封閉多邊形, 其起點為 (100, 10), 終點為 (160, 30), 並通過 (120, 70) 及(200, 110) 兩點, 最後此方法會在起點與終點之間補上一條直線。 其結果如右圖:
DrawString 繪製文字, 其語法如下: 使用指定的 Brush 和 Font 物件, 將文字字串繪製於指定位置 (x, y), 其中有關 Brush 和 Font 類別在 16-3 節有進一步說明, 在此可先略過。例如, 以下敘述可於座標 (100, 50) 的位置繪製文字。 其結果如下圖:
FillRectangle 繪製一個填滿顏色的矩形, 其語法如下: 其中 brush 為筆刷物件, 有關 Brush 類別在下一節中有進一步的說明, (x, y) 為矩形的左上角點, width 為矩形的寬度, height 為矩形的高度。例如,以下敘述可於表單上繪出一個左上角位於 (50, 30), 寬度為 200, 高度為 100,且以紅色填滿的矩形。 其結果如右圖。
FillEllipse 繪製一個填滿顏色的橢圓, 其語法如下: 在指定左上角座標 (x, y), 寬度 (width) 及高度 (height) 的矩形內繪製一 個填滿顏色的橢圓。例如, 以下敘述可繪出一個填滿藍色的橢圓。 其結果如右圖。
FillPie 繪製一個填滿顏色的扇形, 其語法如下: 以上語法中各項參數的意義, 除 brush 物件外, 其它各項參數意義同 DrawPie 方法。例如, 以下敘述可繪出一個填滿黃色的扇形。 其結果如右圖。
FillPolygon 繪製一個填滿顏色的封閉多邊形, 其語法如下: 將指定 Point 結構陣列所定義多邊形內部填滿顏色。例如, 以下敘述可繪出一個填滿綠色的多邊形。 其結果如右圖。
Clear 清除整個繪圖介面, 並使用指定的背景顏色填滿它。其語法如下: 其中 Color 為繪圖介面的背景顏色, 有關 Color 結構在 16-3 節有進一 步的說明。例如, 以下敘述可清除表單之繪圖介面, 並以表單的背景顏色將其填滿。
1 6 - 3 繪圖相關類別 Pen 類別 本單元將介紹的是一些與繪圖相關的類別, 它們分別是 Pen、Brush 、 Color 及 Font 類別, 雖然這些類別並不具有直接繪圖的能力, 但卻是繪圖方法必備的參數。以下將針對這四個繪圖相關類別作進一步的介紹。 Pen 類別 Pen 類別可協助設定畫筆的寬度、顏色、填滿的樣式、線條樣式以及線條端點樣式, 請看以下說明。 Pen 的建構子如下圖所示: 以下僅介紹第四種建構子, 此建構子語法如下:
建構子 實例屬性 以指定畫筆顏色與寬度, 初始化 Pen 類別的執行個體。例如, 以下敘 Width 取得或設定畫筆寬度。例如, 以下敘述可將 drawPen 畫筆物件的筆寬設定為 5。
Color 取得或設定畫筆顏色。例如, 以下敘述可將 drawPen 畫筆物件的畫筆顏色設定藍色。 DashStyle 取得或設定線條的樣式, 此樣式必須是 DashStyle 列舉型別的成員, 如下表所示。 例如, 以下敘述可將 drawPen 畫筆物件的線條樣式設定為虛線。
StartCap 與 EndCap 此外, DashStyle 列舉型別屬於 System、Drawing 及 Drawing2D 命名空 間, 讀者可於程式開頭使用先行宣告, 以避免鍵入全名。 StartCap 與 EndCap 取得或設定線條的端點樣式, 此樣式必須是 LineCap 列舉型別的成員,如下表所示。
例如, 以下敘述可將 drawPen 畫筆物件的線條端點設為箭頭及菱形。
Brush 類別 我們曾經提過, Graphics 類別像是一塊畫布, Pen 類別像是一支畫筆, 但 是這支畫筆只具有畫直線及外框(例如, 橢圓形及扇形)的能力, 若要對某一 塊區域進行填色的動作, Pen 類別就沒有辦法做到了, 而Brush 類別就是用 來對各種封閉圖形填色的工具。針對各種需要, GDI+ 提供了五種 Brush 的 衍生類別, 分別是 SolidBrush(單色)、TextureBrush(材質)、HatchBrush 、 PathGradientBrush 以及 LinearGradientBrush(漸層)等, 以下僅針對 SolidBrush(單 色)與 TextureBrush(材質)類別作進一步的介紹。 SolidBrush 類別 此類別是 Brush 類別中最基本的一種, 用來將某一區域填入單一顏色。 其建構子如下: 以指定色彩初始化 SolidBrush 的執行個體。例如, 以下敘述可在一個 左上角位於 (50, 30), 寬度 100, 高度 50 的矩形內填入紅色。
TextureBrush 類別 TextureBrush 類別是將圖檔填入某一區域, 圖檔必須藉由 Image 類別來 在一個左上角位於 (40, 0), 寬度為 300, 高度為 200 的矩形內填入檔名為 "c:\CsBook\a01.bmp" 的圖檔。
Color 結構 靜態屬性 Color 結構定義 了許多常用的色彩 屬性, 例如, Black 、 Blue、Red 及 Yellow 等, 由於數目相當多, 在此並不一一介紹, 讀者可自行線上查 詢。右圖是筆者線 上查詢的結果。
靜態方法 此外, 由於這些屬性都是靜態屬性, 所以可用" 結構名稱.屬性" 的方 式來取得屬性值。例如, 以下敘述可將畫筆物件設為紅色, 並繪出一條起 點為 (10, 30), 終點為 (200, 30), 寬度為 20 的直線。 靜態方法 FromArgb 由四個 8 位元 ARGB 元件 (Alpha (色彩的透明度) 、紅 (R)、綠 (G) 和藍 (B)) 值建立 Color 結構。共有 4 種多載, 以下僅介紹常用的兩種, 分別說明如下:
public static Color FromArgb(int red, int green, int blue); 之間, 在此多載中 Alpha (色彩明度) 值預設為 255 (完全不透明)。例如, 以下敘述可將 drawPen 畫筆物件設定為紅色, 並繪出一條起點為 (10, 60), 終點為 (200, 60) 的直線。 又例如, 以下敘述可將 drawPen 畫筆物件設定為綠色, 並繪出一條起點為 (10, 90), 終點為 (200, 90) 的直線。
public static Color FromArgb(int alpha, int red, int green, int blue); 從四個 ARGB 元件 (Alpha、紅、綠和藍) 值建立 Color 結構, 有效值在 0-255 之間。其中 Alpha 值表示色彩透明度, 即色彩與背景色彩混合的 程度, 0 表示完全透明的色彩, 255 表示完全不透明的色彩。例如, 以下 敘述可將 drawPen 畫筆物件設定為透明度為 120 的紅色, 並繪出一起 點為 (10, 120), 終點為 (200, 120) 的直線 (請與前一多載所繪出之紅色 直線作顏色上比較)。 又例如, 以下敘述可將 drawPen 畫筆物件設定為透明度為 120 的綠色, 並繪出一起點為 (10, 150), 終點為 (200, 150) 寬度為 20 的直線 (請與前一多載所繪出之綠色直線作顏色比較)。
Font 類別 建構子 Font 類別常用的建構子如下:(線上查詢時, 請點選 System.Drawing 命 名空間的 Font 類別) familyName 是字型名稱, emSize 是字型大小, Style 是字型樣式。其中字 型樣式必須是 FontStyle 列舉型別的成員, 如下表所示。
例如, 以下敘述可建立一個 f 物件, 其字型是名稱是"新細明體", 字型大小是 14, 字型樣式是斜體。 其次, 字型樣式可與 or (|) 運算子相結合, 同時設定多個字型樣式。例如, 以下敘述可建立一個 f1 物件, 其字型名稱是"新細明體", 字型大小是14, 字型樣式是粗斜體。
16-4 實例研究 範例 16_4a 小畫家之研究。 前面已經研究各種繪圖方法, 筆者觀察小畫家的畫直線方式如下: 1. 點選直線圖項。 2. 移至直線起始點。 3. 拖曳直線 (拖曳的過程均不斷呈現繪圖結果) 4. 直到使用者放開滑鼠, 結束直線的繪製, 此時電腦呈現唯一一條直線。 經由以上的觀察, 乃激起筆者實作以上程式的動機, 以下則是筆者的 解題過程。
解題步驟 1. 當使用者按下滑鼠左鍵時, 建立一個繪圖物件, 記錄直線的繪圖起點,並設定 md(mousedown) 旗號為真。 2. 當使用者移動滑鼠時, 此時有三種情況, 第一是還沒有按下滑鼠左鍵; 第二是已經按下滑鼠左鍵, 且是第一次移動滑鼠;第三種是已經按下 滑鼠左鍵, 且是已經移動過滑鼠。首先, 若是僅移動滑鼠, 但還沒按下 滑鼠, 當然是不予理會。可能是使用者還在尋找繪圖起點。其次, 若 是已經按下滑鼠左鍵, 且是第一次移動滑鼠, 則應畫出此直線。此直 線的起點是剛剛按下滑鼠左鍵時所記錄的 point1, 終點則是目前滑鼠 的所在位置, 且設定滑鼠已經移動的旗號 mm (mousemove) 為真。以上 說明的程式如下:
第三種是已經按下滑鼠左鍵, 且是已經移動過滑鼠。這一種情況, 表示使用者還在尋找適當的結束點, 此時要不斷的將剛剛的直線 以白色的筆擦掉, 並畫一條新的直線, 且將此次畫線的結束點記 錄下來, 以便將來可以擦掉。以上說明所需程式如下: 3. 當使用者放開滑鼠左鍵時, 應將 md(mousedown) 旗號設為 false。以上說明程式如下: 4. 為使一些變數流通各事件, 設定全域變數如下:
5. 整體程式列印如下:
執行結果 以下是筆者所繪製的連續直線, 操作方式同小畫家, 請讀者自行開啟檔案練習。 補充說明 1. 本例並未於直線按鈕撰寫任何程式, 那是因為本例僅處理直線, 如果像 小畫家那樣, 必須同時處理矩形、圓形、任意多邊行等圖形, 那就必 須在此設定旗號, 以作為處理各項方法的依據。