Paint Sessions with GDI+
The heart of GDI+ programming is the System.Drawing.Graphics class. The Graphics class encapsulates a GDI+ drawing surface whether it is a window or print document. You paint on the GDI+ drawing surface using a combination of the Graphics class methods.
Accessing the Graphics Object
There are essentially two ways to access a live instance of the Graphics class. In many of the examples you've looked at so far, the painting logic is performed inside a dedicated Paint event handler. In this case, the Graphics object is provided as a parameter to an event handler.For example, the code that follows draws a curve onto a form using the Graphics.DrawArc() method (see Figure 12-1):
// This code handles the Form.Paint event.
private void form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Pen drawingPen = new Pen(Color.Red, 15);
e.Graphics.DrawArc(DrawingPen, 50, 20, 100, 200, 40, 210);
}

Figure 12-1: Painting to a GDI+ surface
You could perform the same task by overriding the OnPaint() method, which is the best approach for an owner-drawn control.
// This code overrides the base Form.OnPaint() method.
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.Paint(e);
Pen drawingPen = new Pen(Color.Red, 15);
e.Graphics.DrawArc(DrawingPen, 50, 20, 100, 200, 40, 210);
}
You don't have to wait for a Paint event to occur. Instead, you can directly obtain the GDI+ Graphics object for a control or an object using the Control.CreateGraphics() method. However, a GDI+ device context uses system resources, and you should make sure to call the Graphics.Dispose() method if you obtain it directly. (In a Paint event handler, you can assume that the .NET framework acquires and disposes of the graphics device context for you.)Here's an example that draws the same arc shown in Figure 12-1, but this time in response to a button click.
private void button1_Click(object sender,
System.EventArgs e)
{
Pen drawingPen = new Pen(Color.Red, 15);
Graphics gdiSurface = this.CreateGraphics();
gdiSurface.DrawArc(DrawingPen, 50, 20, 100, 200, 40, 210);
gdiSurface.Dispose();
}
However, this code isn't equivalent in every respect. You'll discover that as soon as you minimize or hide the window, the arc disappears and is not repainted until you click the button again. To understand why this discrepancy exists, you need to take a closer look at how the Windows operating system handles paint operations.
Painting and Refreshing
The Windows operating system does not store the graphical representation of a window in memory. This architecture stems from the early days of Windows programming when memory was scarce. Storing a bitmap image of every open Window could quickly consume tens of megabytes, and cripple a computer.Instead, Windows automatically discards the contents of a window as soon as it is minimized or hidden by another window. When the program window is restored, Windows sends a message to the application, compelling it to repaint itself. In a .NET application, this means that the Paint event will fire. Similarly, if part of a window is obscured, only those controls that are affected fire Paint events when they reappear on the screen.What this all boils down to is that it's the responsibility of the application (and hence the programmer) to repaint the window when needed. With Paint event handlers, your painting logic will be triggered automatically at the right time. However, if you perform painting inside another method, the result of your work will be lost unless you take specific steps to restore the window after it is hidden or minimized.The best approach is to code around this limitation so that all painting is performed in the Paint event handler. The examples from Figure 12-2). However, though the repainting is triggered by the selection, the code still resides in the Paint event handler.

Figure 12-2: Painting font text
Here's how it works. The SelectedIndex event for the list control uses the Control.Invalidate() method. This tells Windows that the window needs to be repainted. It then sends a message to the window, which the .NET framework translates into a paint event.
private void lstSize_SelectedIndexChanged(object sender,
System.EventArgs e)
{
this.Invalidate();
}
In the Paint event handler, the code reads the font selection and size from the appropriate controls and draws the text in the appropriate font.
private void FontForm_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if (lstFonts.SelectedIndex != -1)
{
try
{
e.Graphics.DrawString(lstFonts.Text, new Font(lstFonts.Text,
int.Parse(lstSize.Text)), Brushes.Black, 10,
50);
StatusBar.Panels(0).Text = ";
}
catch (Exception err)
{
statusBar.Panels[0].Text = err.Message;
}
}
}
Note that there is no way to "clear" content that you've drawn. You can only paint over it, or invalidate the window, at which point the entire window is repainted from scratch.In a more complicated application you could use form-level variables to track the drawing content. Then, an event handler can set these variables and invalidate the form, letting the Paint event handler take care of the rest. This technique is demonstrated a little later in this chapter.