views:

192

answers:

4

Hi, 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  A: 

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.

Jamie Keeling
I know, and that's why I am passing the bitmap to the Progress Changed Event by cloning it, as cross thread gui updates merely result in exceptions due to cross-thread accesses.I just want to know if anyone has succeeded doing something similar
Jean Azzopardi
My apologies, i personally do not have much experience with GUI components.If the current method you have works then it is probably best to stick with it, trying to find an alternative may make it perform worse and it seems you are doing it perfectly fine.
Jamie Keeling
+2  A: 

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.

TheObjectGuy
+1  A: 

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.

Ash
The image is about 640x480, and it is pretty much guaranteed to stay that way.Thanks though.
Jean Azzopardi
+1  A: 

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;
Hans Passant
Thanks, however, we are drawing to the screen about 23 times a second :S That flickers like hell..
Jean Azzopardi
Then you don't really need to worry that the image may disappear.
Hans Passant
Oh, but not with a PictureBox instead of a panel. Thanks for the tip!
Jean Azzopardi
Yeah, a PB has DoubleBuffer = true, a Panel doesn't.
Hans Passant