Why does changing BackgroundImage in Paint Event cause recursion?

ATgeek

New member
Joined
May 17, 2007
Messages
3
Programming Experience
10+
I have a User Control that contains a PictureBox control.

In the User Control's load event I create a bitmap the same size as the PictureBox's ClientRectangle. I also create a Graphics to draw on the bitmap. I set the PictureBox's BackgroundImage property equal to the bitmap.

In the PictureBox's paint event I clear the bitmap, draw on it, and set the PictureBox's BackgroundImage property equal to the bitmap. Then I use e.Graphics to draw on the PictureBox's foreground. The stuff drawn on the bitmap can change each time it is redrawn.

The image that appears in the PictureBox's background is always the bitmap that existed at the beginning of the paint event. The only way I can get it to show the new image is to set BackgroundImage to Nothing before I set it to the new image. But this causes the Paint event to be constantly raised (as evidenced my Debug.Print statements at the start and end of the event handler).

I can draw my bitmap before I invalidate the PictureBox, and that may be what I have to do. But, I still want to understand what is happening here, and if there is a better way to do this.

The attached file is a simplified program showing the problem. <edited to include code inline>
VB.NET:
Public Class Form1
  Private mgfxMyBitmap As Graphics
  Private mbmpMyBitmap As Bitmap

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    MyPictureBox.Size = New Size(200, 200)
    mbmpMyBitmap = New Bitmap(MyPictureBox.ClientRectangle.Width, MyPictureBox.ClientRectangle.Height)
    mgfxMyBitmap = Graphics.FromImage(mbmpMyBitmap)

    MyPictureBox.BackgroundImageLayout = ImageLayout.None
    MyPictureBox.BackgroundImage = mbmpMyBitmap
  End Sub

  Private Sub MyPictureBox_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyPictureBox.Paint
    Debug.Print("Paint started")

    'Compose a background image on the bitmap
    mgfxMyBitmap.Clear(Color.Beige)
    mgfxMyBitmap.DrawEllipse(Pens.Red, 50, 50, 100, 100)

    'Set the picturebox's BackgroundImage to the bitmap
    MyPictureBox.BackgroundImage = Nothing    'The background image only appears if the this line is active
    MyPictureBox.BackgroundImage = mbmpMyBitmap

    'Draw some stuff in the picturebox's foreground 
    e.Graphics.DrawLine(Pens.Green, 50, 50, 100, 100)
    e.Graphics.DrawLine(Pens.Purple, 150, 50, 50, 100)
    Debug.Print("Paint done")
  End Sub

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    mgfxMyBitmap.Clear(Color.Yellow)
    mgfxMyBitmap.DrawEllipse(Pens.Black, 40, 40, 60, 120)
    MyPictureBox.Invalidate()
  End Sub

  Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    mgfxMyBitmap.Clear(Color.Tan)
    mgfxMyBitmap.DrawRectangle(Pens.Red, 60, 60, 80, 80)
    MyPictureBox.Invalidate()
  End Sub
End Class
 
Last edited by a moderator:
Changing BackgroundImage calls for Invalidate on the control so that cause Paint event also. Reason is the control may only consist of background that have to redraw fully when that image is changed, what is drawn on top in Paint event later can't be detected and excluded, so Paint event have to follow the background redraw, thus the whole control must redraw.

It is redundant to change background image every Paint anyway, you might as well draw that directly to the control with e.Graphics.
 
Okay. That explains what is happening. Thanks for the explanation.

In my application the background image may change frequently, even if it is not with every paint event. I will control redrawing the background with global flags. It is necessary to do it this way because the foreground changes more frequently and this saves having to redraw the background each time. The background is more complicated than the foreground.
 
Back
Top