Draw image on a form from a separate thread

你说的曾经没有我的故事 提交于 2019-12-24 03:00:58

问题


I am currently working on a Windows.Forms application. It's basically a simple motion detection problem.

I have a button on a form, that when pressed launches a background worker that does the following :

  1. Fetch an image from disk
  2. Create a new bitmap, to be used as the buffer.
  3. Perform Motion Detection
  4. From the results of Motion Detection, update the buffer (using the buffer's drawing surface)
  5. Fire the Progress Changed Event with an argument consisting of a clone of the buffer, basically (sender as BackgroundWorker).ReportProgress((Bitmap)buffer.Clone())

In the Progress Changed Event, I then draw the buffer to screen.

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

I can't help wondering if this is the best way to draw the updated image on the screen. Can anyone suggest any improvements I can make? Thanks.

EDIT : I have since updated the program to use the .NET Framework 4, and we're no longer using a BackgroundWorker. Instead, we are now using the System.Threading.Tasks namespaces, and using Invoke to update the background image from within the task.

Thanks to all replies.


回答1:


I believe the root of any problems you may be experiencing is the fact that any GUI updates must be done on the UI thread. You cannot safely update the UI from another thread. So, basically, you need to do something like the following (I'm just changing the background color as an example, but you can do whatever you like):

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

    private delegate void DoUpdatePanel(Color aColor);

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

============ Update =======>

@Ash you have mischaracterized my answer. I did not say to call Invoke from within ProgressChanged. @Jean keep in mind that ReportProgress/ProgressChanged is being run asynchronously--which is why you find yourself making a clone of your image. This would not be necessary if you use Invoke from within your background thread, rather than ReportProgress.




回答2:


I'm not sure if this is strictly true but I'm sure you can't cross thread GUI/Control operations on a separate thread as it is handled by default on a dedicated GUI thread.

I tried to do something similar to this before and in the end i decided on an entirely different approach to it as setting a property to false was the worst way to get it to work.




回答3:


The ProgressChanged and RunWorkerCompleted Events allow you to update the UI directly. It is only the DoWork event handler that you must not access the from. See MSDN:

You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.

This is one of the major benefits in using the BackgroundWorker over creating your own thread. So TheObjectGuy is not correct, you do not need to use BeginInvoke/Invoke in ProgressChanged.

As long as your image is not too large, cloning it should not cause any serious performance issues. Run some performance tests with bigger images if you have concerns.

Otherwise, to avoid tricky synchronization issues such as using lock, I think making a clone of the image is a good way to keep things simple.




回答4:


Using the ProgressChanged event is fine. What is not fine is drawing directly to the screen. Your image will disappear when you minimize and restore the form. The workaround is simple:

 PnlImage.BackgroundImage = buffer;


来源:https://stackoverflow.com/questions/2035087/draw-image-on-a-form-from-a-separate-thread

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!