0

I'm creating a WinForms app with picture boxes which are disabled and not visible by default. When I click on a radio button in my form, I want the picture boxes to appear and immediately after that I want something to be drawn over them:

// the radio button CheckedChanged event handler:
table1PictureBox.Enabled = true;
table1PictureBox.Visible = true;
DrawCorrectAnswers();  // draw something over the picture box

The problem is that the drawing finishes before the picture is made visible, so the drawing is ultimately covered by the picture.

While solving the problem I read here that after Visibility is set to true, the actual image load is queued in the message queue of the form. The answer even suggests that a possible solution is to set a timer and then asynchronously wait for its tick and then do the drawing, so that the pictures have time to load. I don't like the solution with setting a timer, instead I would like to wait for the pictures themselves to be loaded.

Is there a way to do this? How exactly does setting Visible to true work in this case?


I also tried to come up with an alternative solution which looked like this:

// the radio button CheckedChanged event handler:
table1PictureBox.Enabled = true;
table1PictureBox.Visible = true;
this.BeginInvoke(new Action(() => { DrawCorrectAnswers(); }));  // 'this' is the form

My idea was that this would enqueue the message for drawing after the message for loading, so even the operations would be performed in the required order. This, however, didn't work either.

In this case, might there be a special behavior of BeginInvoke if I'm in the thread of the form? I even tried normal Invoke and to my surprise, it didn't cause a deadlock. Why is that?


[EDIT] Here is a minimal example that illustrates the problem:

public Form1()
    {
        InitializeComponent();

        pictureBox1.Visible = false;
        pictureBox1.Enabled = false;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        pictureBox1.Enabled = true;
        pictureBox1.Visible = true;

        Graphics graphics = pictureBox1.CreateGraphics();
        graphics.DrawLine(Pens.Black, 0, 0, 50, 50);
    }
honzukka
  • 3
  • 3

1 Answers1

2

The problem here is you are drawing on the picturebox, not on the image, whenever the control gets redrawn everything you have draw on it will be erased and you need to redraw it.

The better solution is to load manually the image, draw the text on the image and then set it to the picturebox:

private void button1_Click(object sender, EventArgs e)
{

    Bitmap bmp = Bitmap.FromFile(pathToTheFile);

    using(var graphics = Graphics.FromImage(bmp))
        graphics.DrawLine(Pens.Black, 0, 0, 50, 50);

    var oldImg = pictureBox1.Image;
    pictureBox1.Image = bmp;

    if(oldImg != null)
      oldImg.Dispose();

    pictureBox1.Enabled = true;
    pictureBox1.Visible = true;

}

Note some things: dispose always any Graphics object you have created, and better surround it with a using block. Also, dispose any unused image when it's not needed, that's why I retrieve the old image and dispose it if exists.

Finally, if you don't want to include the image as a physical file you can embedd it as a resource, there are plenty of examples on how to do it.

EDIT:

What happens under the hood when you set Visible to true is that the PictureBox area is invalidated on the form, then on the next Draw cycle the form will test which visible controls intersect with that rectangle (or any other invalidated area) and then will draw them.

Also, about the Invoke, why it should cause a deadlock? you aren't using any lock, when you call Invoke it will check the thread, if the thread is the UI one then it will execute the function, else it would post the call to the UI thread and the calling one would be blocked until the UI one has processed the function call.

Gusman
  • 14,905
  • 2
  • 34
  • 50
  • Thank you, this makes a lot more sense than my approach! – honzukka Aug 01 '17 at 12:47
  • @honzukka If the answer was correct please accept it. – Gusman Aug 01 '17 at 13:45
  • I would still like to get answers to the additional questions I had. I understand that your approach works the way I need but I still don't understand what exactly was wrong with my attempts. There are some concepts that are still unclear to me. Did I word the question incorrectly, then? (As you can see, this is my first post here... :D ) – honzukka Aug 01 '17 at 14:38