views:

66

answers:

3

I have a FileSystemWatcher set to pick up an picture that will be dropped in a particular directory. The way I was handling it was to add a PictureBox in code that is docked inside of a panel. I ran it, it blew up, and I realized that I was not handling the interaction with the controls on the main thread correctly. Here is the code:

        PictureBox pb = new PictureBox();
        pnlCapturePicture.Controls.Add(pb);
        pb.Dock = DockStyle.Fill;
        pb.ImageLocation = photopath;

Now I understand how to make [Thread-Safe Calls to Windows Forms Controls][1] but I am curious if I just make the Panel Add thread safe am I really accomplishing anything?

Say if I did this:

        PictureBox pb = new PictureBox();
        AddControlThreadSafe(pb);
        pb.Dock = DockStyle.Fill;
        pb.ImageLocation = photopath;

Is interacting with the PictureBox control after it is added to the panel really thread safe?

+1  A: 

Set Control::CheckForIllegalCrossThreadCalls to true in the beginning of the program. In this case, every cross-thread operation with Windows Forms immediately crashes the program. This is better than default undefined behavior, when CheckForIllegalCrossThreadCalls is false.

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.checkforillegalcrossthreadcalls.aspx

Alex Farber
The default on my controls appears to be True.
Flory
+1  A: 

No that will not work. Well, at least it will not work consistently. It might work for awhile, but eventually it will fail unpreditably and spectaculary. The general rule is that you cannot do anything to a Form or Control on any other thread than the one it was created on. In other words, they have thread affinity. What you really need to do is have the main UI thread create and modify the PictureBox by marshaling a message into it. This can be done by taking advantage of the ISynchronizeInvoke methods. All forms and controls implement this interface.

public void ThreadMethod()
{
  pnlCapturePicture.Invoke((Action)(() =>
  {
    PictureBox pb = new PictureBox(); 
    pnlCapturePicture.Controls.Add(pb); 
    pb.Dock = DockStyle.Fill; 
    pb.ImageLocation = photopath; 
  }));
}
Brian Gideon
+2  A: 

No, it will not work. All GUI code must be done on the appropriate user interface thread. The thread context is not checked all the time, so it's possible to write something like that which will work now but fail on a future .NET framework update.

In your case FileSystemWatcher understands the ISynchronizeInvoke pattern, so just set its SynchronizingObject property to the form it works with. Note that if you put a FileSystemWatcher on a form using the designer, this property is set automatically.

Stephen Cleary
Thanks Stephen, you answered the question that I didn't even ask but should have!
Flory