When part of a control must be redrawn, it generates a Paint event. For example, if you minimize a form and then restore it, partially cover a form with another form, or enlarge a form, parts of the form must be redrawn.
The Paint event handler provides a parameter e of type PaintEventArgs. That parameter’s Graphics property holds a reference to a Graphics object that the event handler should use to redraw the control. This Graphics object has its clipping region set to the part of the control that must be redrawn. For example, if you make a form wider, the Graphics object is clipped, so it only draws on the new piece of form on the right that was just exposed. Clipping the Graphics object makes drawing a faster because the GDI+ routines can ignore drawing commands outside of the clipping region more quickly than it can draw them.
Clipping the Graphics object sometimes leads to unexpected results, particularly if the Paint event handler draws something that depends on the form’s size. The following code draws a rectangle with an X in it, filling the form whenever the form resizes:
Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint e.Graphics.Clear(Me.BackColor) e.Graphics.DrawRectangle(Pens.Black, 0, 0, _ Me.ClientSize.Width - 1, _ Me.ClientSize.Height - 1) e.Graphics.DrawLine(Pens.Black, 0, 0, _ Me.ClientSize.Width - 1, _ Me.ClientSize.Height - 1) e.Graphics.DrawLine(Pens.Black, _ Me.ClientSize.Width - 1, 0, _ 0, Me.ClientSize.Height - 1) End Sub
Figure 20-22 shows the result after the form has been resized several times. Each time the form resizes, the Paint event handler draws the newly exposed region, but the existing drawing remains, giving the appearance of stacked envelopes.
Figure 20-22: Paint event handlers that adjust their drawings based on the form’s size may produce unexpected results.
Some computers generate Paint events every time the mouse moves during a resize, so the newly exposed areas are filled with a densely packed series of lines.
Paint event handlers also don’t execute when the form shrinks. If the form shrinks, no new areas are exposed, so no Paint events fire.
Paint event handlers work well if the image on the control does not depend on the control’s size. For example, if you want to draw an ellipse with bounds 10 <= X <= 300, 10 <= Y <= 10, then a Paint event handler works nicely.
If a drawing depends on the control’s size, you must also draw the picture in the control’s Resize event handler. The Resize event handler does not receive a Graphics object representing the control as a parameter, however, but you can use the control’s CreateGraphics method to make one.
If you only draw in the Resize event handler, the image will not refresh if the control needs repainting. The solution is to draw the control in both the Paint and Resize event handlers. To avoid duplicating code, you can move the drawing statements into a separate subroutine and then call it from both the Paint and Resize event handlers. Because the Paint event handler provides a Graphics object for drawing, but the Resize event handler does not, you should pass the Graphics object on which this routine should draw into the routine. The following code shows an improved version of the previous example. Its Paint event handler calls subroutine DrawGraphics, passing it the event handler’s Graphics object. The Resize event handler calls DrawGraphics, passing it a new Graphics object created for the form. Subroutine DrawGraphics uses its Graphics object parameter to draw a rectangle and an X filling the form.
Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint DrawGraphics(e.Graphics) End Sub Private Sub Form1_Resize(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Resize DrawGraphics(Me.CreateGraphics()) End Sub Private Sub DrawGraphics(ByVal gr As Graphics) gr.Clear(Me.BackColor) gr.DrawRectangle(Pens.Black, 0, 0, _ Me.ClientSize.Width - 1, _ Me.ClientSize.Height - 1) gr.DrawLine(Pens.Black, 0, 0, _ Me.ClientSize.Width - 1, _ Me.ClientSize.Height - 1) gr.DrawLine(Pens.Black, _ Me.ClientSize.Width - 1, 0, _ 0, Me.ClientSize.Height - 1) End Sub
When the form shrinks, only the Resize event handler fires. Similarly, if some or all of the form is hidden and then exposed, only the Paint event handler executes.
When the form is enlarged, however, the Resize event fires and draws the entire form. Then the Paint event handler fires and redraws the newly exposed areas. For simple images, this is not a problem. For very complex images that take a long time to draw, the user may see a noticeable delay or flicker.
Visual Basic provides an alternative strategy that allows a form to redraw itself only once when it is exposed or resized. As shown in the following code, first, set the form’s ResizeRedraw property to True to indicate that the form should redraw itself when it is resized. Next use the form’s SetStyle method to set the AllPaintingInWmPaint style to True. This tells Visual Basic that the form does all of its drawing in its Paint event handler. Now, when the form resizes, Visual Basic raises the Paint event in addition to the Resize event, so the program can do all of its drawing in the Paint event and not worry about the Resize event.
Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Me.ResizeRedraw = True Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True) End Sub Private Sub Form1_Paint(ByVal sender As Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint e.Graphics.Clear(Me.BackColor) e.Graphics.DrawRectangle(Pens.Black, 0, 0, _ Me.ClientSize.Width - 1, _ Me.ClientSize.Height - 1) e.Graphics.DrawLine(Pens.Black, 0, 0, _ Me.ClientSize.Width - 1, _ Me.ClientSize.Height - 1) e.Graphics.DrawLine(Pens.Black, _ Me.ClientSize.Width - 1, 0, _ 0, Me.ClientSize.Height - 1) End Sub
Another way to minimize drawing time is to make the drawing on a Bitmap and then set a PictureBox Image property to the result. When part of the PictureBox is exposed, it automatically redisplays the image without needing a Paint event handler. The section “Implementing AutoRedraw” in Chapter 23 describes this technique in greater detail.