Just as GDI is the default graphics interface in the Win32, GDI+, the successor of GDI, is the default graphics interface in .NET. GDI+ is physically stored in the System.Drawing assembly and split into several System.Drawing namespaces:
System.Drawing — the main namespace that contains classes aimed at graphics rendering
System.Drawing.Design — contains classes for developers that are developing design-time user interfaces
System.Drawing.Drawing2D — contains more advanced graphics classes
System.Drawing.Imaging — contains classes that allow us to work with images
System.Drawing.Printing — contains printing-related classes
System.Drawing.Text — contains additional font-related classes
The central object in GDI+ is the Graphics object, which allows the production of graphics output. Unlike the Canvas property, which is always available, the Graphics object must be explicitly created with a call to CreateGraphics(), or you have to write the graphics code in an OnPaint event handler, in which case, the e parameter provides the Graphics object:
private void WinForm_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; }
The CreateGraphics method is not part of the Graphics class. Instead, it is a part of the Control class, and you call it on the control on which you want to paint.
For instance, if you want to paint on the form, you can write this:
Graphics g = this.CreateGraphics(); try { } finally { g.Dispose(); }
or
using(Graphics gr = this.CreateGraphics()) { }
Notice the usage of the reserved word using. Besides its use as a namespace importer, the reserved word using can be used to create a special block at the end of which the Dispose method of the used object will be called. Essentially, the above try-finally and using block do the same thing: dispose of the Graphics object once it is no longer needed.
To draw shapes and lines in GDI+, you have to use pens and brushes and the methods available in the Graphics class. Again, GDI+ does not provide a constant Pen or Brush object like VCL's Canvas does, so if you want a custom pen or brush, you'll need to create a custom pen or brush object. But if you need a plain pen or brush, like a 1px wide black pen, you can use the static pens and brushes from the System.Drawing.Pens and the System.Draw ing.Brushes classes.
The following listing shows how to produce simple graphics output with GDI+ in a C# Windows.Forms application. It shows how to paint inside the OnPaint event, create a new Graphics object with a call to CreateGraphics, use static pens and brushes, create custom pen objects, and use the using block to release graphics object when you're done with them.
You can see the result of the code from Listing 30-16 in Figure 30-6.
Figure 30-6: GDI+ essentials
Listing 30-16: GDI+ essentials
private void WinForm_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; Rectangle rc = new Rectangle(20, 20, 200, 200); // create and use a custom pen (Color, Width) using(Pen p = new Pen(Color.Blue, 2)) { // use the static White brush from the Brushes class g.FillRectangle(Brushes.White, rc); // use the custom pen g.DrawRectangle(p, rc); } } private void WinForm_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { if(e.Button != MouseButtons.Left) return; using(Graphics g = this.CreateGraphics()) { // top, left, width, height g.FillRectangle(Brushes.Chocolate, e.X - 2, e.Y - 2, 4, 4); } // the g object gets disposed here }
Besides being able to display shapes, lines, and curves, GDI+ allows us to, for instance, draw gradients and semitransparent shapes, and work with a larger number of image formats.
The following example shows how to load and display images, use the System.Drawing.Drawing2.LinearGradientBrush class to draw gradients, fill text with a texture using the System.Drawing.Drawing2D.TextureBrush class, and antialias the text with the help of the TextRenderingHint property. The example is thoroughly commented and not very complex, so there should be no problem understanding how it works. Figure 30-7 shows the graphics output produced by the code in Listing 30-17.
Figure 30-7: Using more advanced GDI+ classes
Listing 30-17: Working with images, text, texture, and gradient brushes
private string dir; private System.Drawing.Image img; private System.Drawing.Image texture; [STAThread] static void Main() { Application.Run(new WinForm()); } private void WinForm_Load(object sender, System.EventArgs e) { // System.IO.Path class, get app path dir = Path.GetDirectoryName(Application.ExecutablePath); // load the image and resize the form // to see the entire image img = Image.FromFile(dir + @"\Rijeka.jpg"); this.ClientSize = new Size(img.Width, img.Height); // load the texture image texture = Image.FromFile(dir + @"\Texture.jpg"); } private void WinForm_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; // System.Drawing.Drawing2D.LinearGradientBrush LinearGradientBrush grad = new LinearGradientBrush(this.ClientRectangle, Color.White, Color.Transparent, LinearGradientMode.Horizontal); // System.Drawing.Drawing2D.TextureBrush TextureBrush tex = new TextureBrush(texture); Font myFont = new Font("Impact", 66, FontStyle.Bold); try { g.DrawImage(img, 0, 0, img.Width, img.Height); g.FillRectangle(grad, this.ClientRectangle); // System.Drawing.Text namespace, // use RenderingHint to display antialiased text g.TextRenderingHint = TextRenderingHint.AntiAlias; g.DrawString("The Adriatic Sea", myFont, tex, 20, 20); } finally { grad.Dispose(); tex.Dispose(); myFont.Dispose(); } // try..finally } // OnPaint