当 PictureBox.Visible 设置为 True 时,C# 中到底发生了什么?
What Exactly Happens in C# When PictureBox.Visible Is Set to True?
我正在创建一个带有图片框的 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);
}
这里的问题是你在图片框上绘制,而不是在图像上绘制,每当重绘控件时,你在其上绘制的所有内容都将被擦除,你需要重绘它。
更好的解决办法是手动加载图片,在图片上绘制文字,然后设置到图片框:
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 区域无效,然后在下一个绘制周期中,窗体将测试哪些可见控件与该矩形(或任何其他无效区域)相交) 然后会绘制它们。
另外,关于Invoke,为什么会导致死锁?你没有使用任何锁,当你调用 Invoke 时它会检查线程,如果线程是 UI 那么它会执行函数,否则它会 post 调用 UI 线程和调用线程将被阻塞,直到 UI 线程处理完函数调用。
我正在创建一个带有图片框的 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);
}
这里的问题是你在图片框上绘制,而不是在图像上绘制,每当重绘控件时,你在其上绘制的所有内容都将被擦除,你需要重绘它。
更好的解决办法是手动加载图片,在图片上绘制文字,然后设置到图片框:
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 区域无效,然后在下一个绘制周期中,窗体将测试哪些可见控件与该矩形(或任何其他无效区域)相交) 然后会绘制它们。
另外,关于Invoke,为什么会导致死锁?你没有使用任何锁,当你调用 Invoke 时它会检查线程,如果线程是 UI 那么它会执行函数,否则它会 post 调用 UI 线程和调用线程将被阻塞,直到 UI 线程处理完函数调用。