我正在创建一个带有图片框的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);
}
答案 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处理完函数调用。