Reputation: 157981
I have a UserControl with something that does some background work. When thing has a completed event, which the user control is subscribed to. Since the event is raised in a separate thread and the event handler does some updating of the user control I start the event handler with this:
private void ProcessProcessCompleted(object sender, ProcessCompletedEventArgs e)
{
if (InvokeRequired)
{
Invoke(new Action<object, ProcessCompletedEventArgs>(ProcessProcessCompleted),
new[] { sender, e });
return;
}
// Update the user control, etc.
}
This works fine, except if the form that contains the user control is closed before the process has completed. I tried to unsubscribe to the event in the dispose method of the user control, but that only seems to work parts of the time. How should I be doing this? Would like to avoid wrapping it in a try catch block. Is there an InvokeIsPossible
property or something I can check before I Invoke?
Upvotes: 1
Views: 1295
Reputation: 47968
You could use the SynchronizationContext
, it was introduced in .NET 2 to simplify inter-thread communication:
initialize it to the form's thread sync'context:
syncContext = SynchronizationContext.Current;
Change your ProcessProcessCompleted
method like this:
private void ProcessProcessCompleted(object sender, object e)
{
this.syncContext.Post(new SendOrPostCallback(delegate(object state)
{
// Update the user control, etc.
})
, null);
}
You can read about SynchronizationContext
here:
http://www.codeproject.com/KB/cpp/SyncContextTutorial.aspx
or http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.aspx
Upvotes: 3
Reputation: 6845
I use a class I call a thread-gate which I usually inherit from but you can just instantiate it or whatever. This uses the SynchronizationContext class.
public class ThreadGate { private readonly SynchronizationContext _synchronizationContext;
public ThreadGate()
{
_synchronizationContext = AsyncOperationManager.SynchronizationContext;
}
public void Post<T>(Action<T> raiseEventMethod, T e)
{
if (_synchronizationContext == null)
ThreadPool.QueueUserWorkItem(delegate { raiseEventMethod(e); });
else
_synchronizationContext.Post(delegate { raiseEventMethod(e); }, null);
}
}
I use it like this:
Post(OnProgressChanged,new ProgressChangedEventArgs(1,null)));
Or if you like Action
Action<string> callback;
Post(callback,str));
Upvotes: 1
Reputation: 4992
Although it doesn't synchronize the threads, you can use Control.IsDisposed
to check whether the Invoke()
method can be called in the current state (Form
inherits from Control
):
if (!this.IsDisposed)
{
// tatata...
}
To guarantee that is works, you must wrap it in a lock(syncobject) { ... }
block to check a custom boolean flag and likewise change that flag in a lock
-statement inside a Form_Close
event.
Upvotes: 0