Drawing to the Screen


No matter what kind of drawing you're doing, the underlying abstraction is the Graphics class from the System.Drawing namespace. The Graphics class provides the abstract surface on which you're drawing, whether the results of your drawing operations are displayed on the screen, stored in a file, or spooled to the printer. The Graphics class is too large to show here, but we return to it throughout the chapter.

One way to obtain a Graphics object is to use CreateGraphics to create a new one that's associated with a form:

bool drawEllipse = false; void drawEllipseButton_Click(object sender, EventArgs e) {   ...   // Toggle whether or not to draw the ellipse   this.drawEllipse = !this.drawEllipse;   using( Graphics g = this.CreateGraphics() ) {      if( this.drawEllipse ) {        // Draw the ellipse        g.FillEllipse(Brushes.DarkBlue, this.ClientRectangle);      }      else {        // Erase the previously drawn ellipse        g.FillEllipse(SystemBrushes.Control, this.ClientRectangle);      }   } }


After we have a Graphics object, we can use it to draw on the form. Because we're using the button to toggle whether to draw the ellipse, we either draw an ellipse in dark blue or use the system color as the background of the form, as illustrated in Figure 5.1.

Figure 5.1. Ellipse Form Before Resizing


Unfortunately, when the form is resized, or covered and uncovered, the ellipse is not automatically redrawn.

Handling the Paint Event

To deal with this, Windows asks a form (and all child controls) to redraw newly uncovered content via the Paint event, whose PaintEventArgs argument provides a Graphics object for us:

namespace System.Windows.Forms {   class PaintEventArgs {     public Rectangle ClipRectangle { get; }     public Graphics Graphics { get; }   } }


Taking advantage of this requires moving the ellipse drawing logic to the Paint event handler:

bool drawEllipse = false; void drawEllipseButton_Click(object sender, EventArgs e) {   this.drawEllipse = !this.drawEllipse; } void DrawingForm_Paint(object sender, PaintEventArgs e) {   if( !this.drawEllipse ) return;   Graphics g = e.Graphics;   g.FillEllipse(Brushes.DarkBlue, this.ClientRectangle); }


By the time the Paint event is fired, the background of the form has already been drawn, so any ellipse that was drawn during the last Paint event will be gone; this means that we must draw the ellipse only if the flag is set to true.[3] However, even if we set the flag to draw the ellipse, Windows doesn't know that the state of the flag has changed, so the Paint event isn't triggered and the form doesn't get a chance to draw the ellipse. To avoid the need to draw the ellipse in both the button's Click event and the form's Paint event, we must request a Paint event and let Windows know that the form needs to be redrawn.

[3] A form or control can draw its own background by overriding the OnPaintBackground method.

Triggering the Paint Event

To request a Paint event, we use the Invalidate method:

void drawEllipseButton_Click(object sender, EventArgs e) {   drawEllipse = !drawEllipse;   // Ask Windows for a Paint event for the form and its children   this.Invalidate(true); }


Now, when the user toggles the drawEllipse flag, we call Invalidate to let Windows know that a part of the form needs to be redrawn. Passing true to the form's Invalidate method ensures that Paint events are fired for the form and its child controls, whereas passing false or nothing at all fires a Paint event only for the form.[4]

[4] Optimized use of the Invalidate method is covered in Chapter 7: Advanced Drawing.

Because drawing is one of the more expensive operations, Windows first handles all other eventssuch as mouse movements, keyboard entry, and so onbefore firing the Paint event, just in case multiple areas of the form need to be redrawn at the same time. To avoid this delay, we use the Update method to force Windows Forms to trigger the Paint event immediately. Because both invalidating and updating the entire client area of a form are common, forms also have a Refresh method that combines the two:

void drawEllipseButton_Click(object sender, EventArgs e) {   drawEllipse = !drawEllipse;   // Either ask Windows Forms for a Paint event   // for both form and children   this.Invalidate(true);   // Or force the Paint event to happen now   this.Update();   // Or do both at once   this.Refresh(); // Invalidate(true) + Update }


However, if you can wait, it's best to let Windows request the Paint event in its own sweet time. It's delayed for a reason: It's the slowest thing that the system does. Forcing all paint operations to happen immediately eliminates an important optimization. However, letting Windows combine paint requests and then handle them in Windows Forms when it's ready results in less drawing and consequently a potentially more responsive application.

If you've been following along with this simple example, you'll be pleased to see that pressing the button toggles nicely whether or not the ellipse is shown on the form, and covering and uncovering the form redraws as expected. However, if you resize the form, you'll be disappointed by the results shown by Figure 5.2.

Figure 5.2. Ellipse Form After Resizing


In Figure 5.2, it seems as if the ellipse has been drawn several times as the form is resized, incompletely each time. What's happening is that, as the form is being expanded, Windows is drawing only the newly exposed area, under the assumption that the existing rectangle doesn't need to be redrawn. Although we're redrawing the entire ellipse during each Paint event, Windows is ignoring everything outside the clip regionthat part of the form that needs redrawingand that leads to the strange drawing behavior. Luckily, you can set a style to request that Windows redraw the entire form during a resize:

// DrawingSampleForm.cs partial class DrawingSampleForm : Form {   public DrawingSampleForm() {     InitializeComponent();     // Trigger a Paint event when the form is resized     this.SetStyle(ControlStyles.ResizeRedraw, true);   } }


Forms (and controls) have several drawing styles (you'll see more in Chapter 7). The ResizeRedraw style causes Windows to redraw the entire client area whenever the form is resized. Of course, this is less efficient, and that's why Windows defaults to the original behavior.




Windows Forms 2.0 Programming
Windows Forms 2.0 Programming (Microsoft .NET Development Series)
ISBN: 0321267966
EAN: 2147483647
Year: 2006
Pages: 216

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net