2

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

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

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

在 Progress Changed Event 中,然后我将缓冲区绘制到屏幕上。

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

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

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

感谢所有回复。

4

4 回答 4

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,则不需要这样做。

于 2010-01-10T02:10:03.667 回答
1

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

您必须小心不要在 DoWork 事件处理程序中操作任何用户界面对象。相反,通过 ProgressChanged 和 RunWorkerCompleted 事件与用户界面进行通信。

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

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

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

于 2010-01-10T05:57:13.367 回答
1

使用 ProgressChanged 事件很好。不好的是直接在屏幕上绘图。当您最小化并恢复表单时,您的图像将消失。解决方法很简单:

 PnlImage.BackgroundImage = buffer;
于 2010-01-11T16:53:02.223 回答
1

我不确定这是否严格正确,但我确定您不能在单独的线程上跨线程 GUI/Control 操作,因为默认情况下它是在专用 GUI 线程上处理的。

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

于 2010-01-10T00:54:53.570 回答