天天看點

像Linq一樣來使用Graphics

Linq的鍊式程式設計用起來總是那樣暢快淋漓,可惜在C#中并不是每時每刻都能有這麼暢快的感覺,其中使用Graphics的時候就是,每次用Graphics繪制大量圖形時尤其如此。GDI+的API功能很強大,但是在實際編碼中,很多重複性的工作總是讓我感覺到用起來很繁瑣,于是我就設計了這樣一個類庫,将C#中的Graphics類進行了二次封裝,讓其可以和Linq一樣,用起來“如沐春風”。

先來看一段簡單的示例代碼吧。下面代碼就是在一個窗體上繪制一系列圖形,可以看出和原來的Graphics相比,編碼量更小,代碼也更優雅。

1 private void Form1_Paint(object sender, PaintEventArgs e)
 2         {
 3             e.Graphics.Ex()
 4                 .DrawLine(10, 10, 50, 50)
 5                 .DrawLine(50, 50, 100, 50,Pens.Red)
 6                 .DrawLine(100, 50, 100, 100)
 7                 .DrawLine(100, 100, 150, 100, new Pen(Color.Blue,3f))
 8                 .DrawLine(150, 100, 150, 150)
 9                 .DrawRectangle(150, 50, 100, 100)
10                 .FillRectangle(150, 50, 100, 100, Brushes.Red)
11                 .DrawEllipse(150, 50, 50, 100, new Pen(Color.Yellow, 3f))
12                 .FillEllipse(150, 50, 50, 100,Brushes.Green)
13                 .DrawString("haha",new PointF(200f,200f))
14                 .DrawString("leilei", new PointF(100f, 200f),new Font("微軟雅黑",30f));
15         }      

畫出來的效果如下:

像Linq一樣來使用Graphics

下面就是我對Graphics二次封裝的具體代碼,目前還隻能繪制Line、Rectangle、Ellipse和string

1 public static class GDIEx
  2     {
  3         public static GraphicsEx Ex(this Graphics g)
  4         {
  5             return new GraphicsEx(g);
  6         }
  7     }
  8     public class GraphicsEx : IDisposable
  9     {
 10         readonly Graphics g;
 11         Pen pen = Pens.Black;
 12         Brush brush = Brushes.Black;
 13         Font font = new Font(FontFamily.GenericSerif,10);
 14         internal GraphicsEx(Graphics g)
 15         {
 16             this.g = g;
 17         }
 18         public void Dispose()
 19         {
 20             g.Dispose();
 21             pen = null;
 22             brush = null;
 23             font = null;
 24         }
 25         public GraphicsEx DrawLine(int x1, int y1, int x2, int y2,[Optional]Pen pen)
 26         {
 27             if (pen != null)
 28                 this.pen = pen;
 29             g.DrawLine(this.pen, x1, y1, x2, y2);
 30             return this;
 31         }
 32         public GraphicsEx DrawLine(Point p1, Point p2,[Optional]Pen pen)
 33         {
 34             if (pen != null)
 35                 this.pen = pen;
 36             g.DrawLine(this.pen, p1, p2);
 37             return this;
 38         }
 39         public GraphicsEx DrawRectangle(Rectangle rect,[Optional]Pen pen)
 40         {
 41             if (pen != null)
 42                 this.pen = pen;
 43             g.DrawRectangle(this.pen, rect);
 44             return this;
 45         }
 46         public GraphicsEx DrawRectangle(int left,int top,int width,int height,[Optional]Pen pen)
 47         {
 48             if(pen != null)
 49                 this.pen = pen;
 50             g.DrawRectangle(this.pen, left, top, width, height);
 51             return this;
 52         }
 53         public GraphicsEx FillRectangle(Rectangle rect, [Optional]Brush brush)
 54         {
 55             if(brush != null)
 56                 this.brush = brush;
 57             g.FillRectangle(this.brush, rect);
 58             return this;
 59         }
 60         public GraphicsEx FillRectangle(int left, int top, int width, int height, [Optional]Brush brush)
 61         {
 62             if (brush != null)
 63                 this.brush = brush;
 64             g.FillRectangle(this.brush, left, top, width, height);
 65             return this;
 66         }
 67         public GraphicsEx DrawEllipse(Rectangle rect, [Optional]Pen pen)
 68         {
 69             if (pen != null)
 70                 this.pen = pen;
 71             g.DrawEllipse(this.pen, rect);
 72             return this;
 73         }
 74         public GraphicsEx DrawEllipse(int left, int top, int width, int height, [Optional]Pen pen)
 75         {
 76             if (pen != null)
 77                 this.pen = pen;
 78             g.DrawEllipse(this.pen, left, top, width, height);
 79             return this;
 80         }
 81         public GraphicsEx FillEllipse(Rectangle rect, [Optional]Brush brush)
 82         {
 83             if (brush != null)
 84                 this.brush = brush;
 85             g.FillEllipse(this.brush, rect);
 86             return this;
 87         }
 88         public GraphicsEx FillEllipse(int left, int top, int width, int height, [Optional]Brush brush)
 89         {
 90             if (brush != null)
 91                 this.brush = brush;
 92             g.FillEllipse(this.brush, left, top, width, height);
 93             return this;
 94         }
 95         public GraphicsEx DrawString(string str, RectangleF rect,[Optional]Font font, [Optional]Brush brush)
 96         {
 97             if (font != null)
 98                 this.font = font;
 99             if (brush != null)
100                 this.brush = brush;
101             g.DrawString(str, this.font, this.brush, rect);
102             return this;
103         }
104         public GraphicsEx DrawString(string str, PointF p, [Optional]Font font, [Optional]Brush brush)
105         {
106             if (font != null)
107                 this.font = font;
108             if (brush != null)
109                 this.brush = brush;
110             g.DrawString(str, this.font, this.brush, p);
111             return this;
112         }
113     }      

封裝思想其實比較簡單,封裝的主體就是類GraphicsEx,該類根據構造函數中傳入的Graphics進行繪圖,并且繪制函數的簽名盡量和Graphics的接口保持一緻,以增加易用性,并且每個繪制函數都會傳回執行個體本身,以供不斷的調用。

所有的畫筆、畫刷或者其它與繪制内容無關的繪制參數都采用可選參數,這樣做的目的很簡單,從文章開始的示例中可以看出,在繪制一些Line的步驟中并沒有指明所用的畫筆,這時Graphics繪制時會自動采用上一次使用的畫筆或者初始畫筆進行繪制,這樣在使用同一種畫筆繪制多條直線,或者繪制多種不同圖形時,可以省去每一步都必須要指定畫筆的工作,減少編碼量。

我對GraphicsEx的構造函數通路級别進行了控制,設定為internal級别,隻讓其在程式集内可見,并且通過建構一個Graphics的擴充方法,用來建立GraphicsEx的執行個體,用來代替其本身構造函數的功能,這樣在使用時就顯得更加自然一些。

就寫這麼多了,不知道大家看完我這樣的封裝有什麼自己的看法,希望不吝賜教,在回帖中和我交流,謝謝!