Svish
Svish

Reputation: 157981

C#: How to prevent Invoke from failing

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

Answers (3)

manji
manji

Reputation: 47968

You could use the SynchronizationContext, it was introduced in .NET 2 to simplify inter-thread communication:

  • create a global SynchronizationContext variable (let's say syncContext)
  • 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

Lee Treveil
Lee Treveil

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

Cecil Has a Name
Cecil Has a Name

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

Related Questions