从单独的线程在表单上绘制图像

时间:2010-01-09 21:48:50

标签: c# winforms motion-detection

我目前正在开发Windows.Forms应用程序。它基本上是一个简单的运动检测问题。

我在表单上有一个按钮,按下后会启动一个执行以下操作的后台工作程序:

  1. 从磁盘中提取图像
  2. 创建一个新位图,用作缓冲区。
  3. 执行动作检测
  4. 根据运动检测的结果,更新缓冲区(使用缓冲区的绘图表面)
  5. 使用包含缓冲区克隆的参数触发Progress Changed事件,基本上是(sender as BackgroundWorker).ReportProgress((Bitmap)buffer.Clone())
  6. 在Progress Changed Event中,然后我将缓冲区绘制到屏幕上。

    if (!PnlImage.IsDisposed)
                PnlImage.CreateGraphics().DrawImageUnscaled(buffer, 0, 0);
    

    我不禁想知道这是否是在屏幕上绘制更新图像的最佳方式。任何人都可以建议我可以做出哪些改进? 感谢。

    编辑: 我已经更新了程序以使用.NET Framework 4,我们不再使用BackgroundWorker。相反,我们现在使用System.Threading.Tasks命名空间,并使用Invoke从任务中更新背景图像。

    感谢所有回复。

4 个答案:

答案 0 :(得分:2)

我相信您可能遇到的任何问题的根源是任何GUI更新都必须在UI线程上完成。您无法安全地从其他线程更新UI。所以,基本上,你需要做类似下面的事情(我只是改变背景颜色作为例子,但你可以做任何你喜欢的事情):

    private void SomethingCalledFromBackgroundThread()
    {
        panel1.Invoke(new DoUpdatePanel(UpdatePanel), Color.Blue);
    }

    private delegate void DoUpdatePanel(Color aColor);

    private void UpdatePanel(Color aColor)
    {
        panel1.BackColor = aColor;
    }

============更新=======>

@Ash你错误地描述了我的答案。我没有说要在ProgressChanged中调用Invoke。 @Jean请记住,ReportProgress / ProgressChanged是异步运行的 - 这就是为什么你发现自己正在复制你的图像。如果您在后台线程中使用Invoke而不是ReportProgress,则不需要这样做。

答案 1 :(得分:1)

我不确定这是否严格正确,但我确信你不能在单独的线程上交叉线程GUI /控制操作,因为它默认在专用的GUI线程上处理。

我之前尝试过类似的事情,最后我决定使用一种完全不同的方法,因为将属性设置为false是让它工作的最糟糕方式。

答案 2 :(得分:1)

ProgressChanged和RunWorkerCompleted事件允许您直接更新UI。只有DoWork事件处理程序才能访问。 See MSDN

  

你必须小心不要操纵   你的任何用户界面对象   DoWork事件处理程序。代替,   与用户界面进行通信   通过ProgressChanged和   RunWorkerCompleted事件。

这是使用BackgroundWorker而不是创建自己的线程的主要好处之一。所以TheObjectGuy不正确,你不需要在ProgressChanged中使用BeginInvoke / Invoke。

只要您的图片不是太大,克隆它就不会导致任何严重的性能问题。如果您有疑虑,请使用更大的图像运行一些性能测试。

否则,为了避免使用锁等棘手的同步问题,我认为复制图像是保持简单的好方法。

答案 3 :(得分:1)

使用ProgressChanged事件很好。什么不好是直接画到屏幕上。最小化和恢复表单时,图像将消失。解决方法很简单:

 PnlImage.BackgroundImage = buffer;