当PictureBox.Visible设置为True时,C#中究竟会发生什么?

时间:2017-08-01 12:01:39

标签: c# winforms picturebox synchronizationcontext

我正在创建一个带有图片框的WinForms应用程序,这些图片框已禁用且默认情况下不可见。当我点击表单中的单选按钮时,我想要显示图片框,之后我想要在它们上面绘制一些内容:

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

问题是图片在图片可见之前完成,因此图片最终会被图片覆盖。

在解决问题时我读了here,在Visibility设置为true之后,实际的图像加载在表单的消息队列中排队。答案甚至建议一个可能的解决方案是设置一个计时器,然后异步等待其刻度,然后进行绘图,以便图片有时间加载。我不喜欢设置计时器的解决方案,而是希望等待图片本身加载。

有办法做到这一点吗?在这种情况下,如何将Visible设置为true?

我还尝试提出一个看起来像这样的替代解决方案:

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

我的想法是,这会在加载消息之后将消息排入队列,因此即使操作也会按所需顺序执行。但是,这也没有用。

在这种情况下,如果我在表单的线程中,可能会有BeginInvoke的特殊行为吗?我甚至尝试了普通的Invoke,令我惊讶的是,它没有造成僵局。那是为什么?

[编辑]这是一个说明问题的最小例子:

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);
    }

1 个答案:

答案 0 :(得分:2)

这里的问题是你正在绘制图片框,而不是在图像上,每当控件重绘时,你绘制的所有内容都将被删除,你需要重新绘制它。

更好的解决方案是手动加载图像,在图像上绘制文本,然后将其设置到图片框:

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;

}

注意一些事情:始终处置您创建的任何Graphics对象,并使用using块更好地包围它。此外,在不需要的时候处理任何未使用的图像,这就是为什么我检索旧图像并在存在时将其丢弃的原因。

最后,如果您不想将图像作为物理文件包含在内,您可以将其作为资源嵌入,那么有很多关于如何实现它的示例。

编辑:

当您将Visible设置为true时,幕后发生的事情是PictureBox区域在窗体上无效,然后在下一个Draw循环中,窗体将测试哪些可见控件与该矩形(或任何其他无效区域)相交然后将绘制它们。

另外,关于Invoke,为什么它应该导致死锁?你没有使用任何锁,当你调用Invoke它将检查线程,如果线程是UI,那么它将执行该函数,否则它会将调用发布到UI线程,调用的将是阻止,直到UI处理完函数调用。