【发布时间】:2015-06-25 15:58:47
【问题描述】:
我这辈子都想不通。我正在修改 PictureBox 指向的位图。基本上有人将图像拖过图片框,并且图像会更新,因此看起来像是拖过图片框。刷新图片框时会发生什么,我在 Application.Run 行上得到一个 ArgumentException Parameter not valid 错误,所以我花了一段时间才弄清楚究竟是什么原因造成的,因为异常中没有更多信息。
我创建了一个派生自图片框的类并覆盖了 onpaint 方法。它还有一个方法 RefreshImage 来调用自己并刷新自己
如果我不刷新图片框,我不会收到此错误,但图片框图像永远不会更新
我有一个更新逻辑的主循环在后面运行。逻辑是当鼠标被拖动时,图像偏移量被更新。
循环完成逻辑后,它会尝试刷新图片框。这是我发现当我注释掉刷新行时,我没有问题,但图片框永远不会更新。我已向名为 UpdateImage 的自定义图片框类添加了一个方法,该方法调用自身然后刷新。
重写的 onpaint 方法从自定义循环线程中获取逻辑,并根据后端循环线程更新的偏移量更新位图
主窗体是在主线程上创建的,而图片框也是在主线程上创建的,我已经检查过了
调用正常工作,刷新图片框时后端线程调用主线程
注释掉刷新行会使错误消失,但看不到图片框更新
那么对于更好的方法或错误的解决方案有什么建议吗?
这是派生的图片框类的一部分
public class PictureBoxEX : PictureBox
{
public int m_xoffset = 0;
private Bitmap m_picture;
public PictureBoxEX()
{
m_picture = new Bitmap(690, 600);
this.Image = m_picture;
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
DrawBitmaps(); // updates the m_picture with another bitmap only in this class
Graphics g = pe.Graphics;
g.DrawImage(this.m_picture, new Point(0, 0));
}
public void RefreshImage()
{
if(this.InvokeRequired)
{
MethodInvoker d = new MethodInvoker(RefreshImage);
this.Invoke(d);
}
else
{
this.Refresh();
}
}
}
这是主循环的想法
private MainForm m_MainWindow; // derrives from Form
private void MainThread()
{
int currTime = System.Environment.TickCount;
int lastTime = currTime;
while (Running)
{
currTime = System.Environment.TickCount;
m_MainWindow.Update(currTime, lastTime);
lastTime = currTime;
Thread.Sleep(10);
}
}
这是更新方法在主窗口中的样子
PictureBoxEx m_LeftPictureBox;
public void Update(int currTime, int lastTime)
{
// do logic (all the logic is only working with primitive types, nothing to do with actual bitmaps
this.m_LeftPictureBox.RefreshImage(); // if i comment this out, i will not get the exception, but it will not update the picturebox
}
}
还有一件事,为什么主窗体刷新时图片框不刷新?或者可能是主要形式不令人耳目一新?我确定如果是这种情况,我会看到光标没有被清除。我在自定义图片框类的onpaint方法中放了一个断点,它只在第一次创建时调用一次,之后就没有了,所以我必须手动刷新它。
编辑: 不确定我可以提供哪些更多代码来帮助解决这个问题,但是如果我从后台线程调用这个函数(这个函数在我制作的表单类中),我几乎立即得到相同的异常。异常发生在 this.Refresh() 和主线程上(所以在它被调用之后)。即使它在一个 try-catch 块中,异常仍然说它未被捕获并关闭我的应用程序
public void InvokeRefresh()
{
if (this.InvokeRequired)
{
RefreshDelegate d = new RefreshDelegate(InvokeRefresh);
this.Invoke(d, null);
}
else
{
try
{
this.Refresh();
}
catch { }
}
}
【问题讨论】:
-
如果将 InvokeRequired 检查放入 Update 方法会发生什么?
-
好问题,我最初尝试过,同样的问题,我只是将调用检查移至自定义类,但要清理更新方法,谢谢
-
请发布完整的堆栈跟踪和可能的更多代码。您的问题在 Application.Run 行中提到了 ArgumentException Parameter not valid 错误。这不应该与多线程问题有关。
-
对于堆栈跟踪来说并不多。它类似于 [外部代码] -> Program.Run() -> [外部代码]
-
因此,如果我不在我的表单或图片框上调用 refresh,则覆盖的 onpaint 只会在首次创建时被调用一次。如果我从后台线程调用表单(调用它)刷新,我会得到相同的异常,但在主线程上的实际刷新调用
标签: c# winforms thread-safety