English 中文(简体)
Draw image on a form from a separate thread
原标题:

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.

最佳回答

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.

问题回答

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.

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.

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;




相关问题
Bring window to foreground after Mutex fails

I was wondering if someone can tell me what would be the best way to bring my application to the foreground if a mutex was not able to be created for a new instance. E.g.: Application X is running ...

How to start WinForm app minimized to tray?

I ve successfully created an app that minimizes to the tray using a NotifyIcon. When the form is manually closed it is successfully hidden from the desktop, taskbar, and alt-tab. The problem occurs ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

Handle DataTable.DataRow cell change event

I have a DataTable that has several DataColumns and DataRow. Now i would like to handle an event when cell of this DataRow is changed. How to do this in c#?

Apparent Memory Leak in DataGridView

How do you force a DataGridView to release its reference to a bound DataSet? We have a rather large dataset being displayed in a DataGridView and noticed that resources were not being freed after the ...

ALT Key Shortcuts Hidden

I am using VS2008 and creating forms. By default, the underscore of the character in a textbox when using an ampersand is not shown when I run the application. ex. "&Goto Here" is not ...

WPF-XAML window in Winforms Application

I have a Winforms application coded in VS C# 2008 and want to insert a WPF window into the window pane of Winforms application. Could you explain me how this is done.

热门标签