views:

63

answers:

2

Within a class library I'm writing I have a method allowing the library to go and do some stuff on a different thread which does something like:

    public void DoStuffAsync(AP p)
    {
        this.Running = true;
        this.Cancel = false;

        ParameterizedThreadStart threadStart = new ParameterizedThreadStart(DoStuff);
        Thread procThread = new Thread(threadStart);
        procThread.Start(p);
    }

I also have a number of events declared on the interface that the developer can hook into, such as StatusUpdate and ProgressUpdate. I'm currently writing a little test app (in WPF presently although I expect the same behaviour in WinForms) that calls DoStuffAsync() and then updates a progress bar and label.

Unfortunately 1st pass I got an error, the usual thread not being the one which owns the controls. What I'd like to do is remove the need for the user to call Invoke() within the UI side, and for them to simply subscribe to the events and have them work.

So the question, is there a way I can do this is my code when dealing with the event handlers? Currently trigger like so:

        public void UpdateProgress(object sender, ProgressEventArgs e)
        {
            if (handler != null)
            {
                handler(sender, e);
            }
        }
+3  A: 

You will need a reference to the client's Dispatcher object so you can call Dispatcher.Invoke or Dispatcher.BeginInvoke to marshal the call to the client's UI thread. Do so by letting the client give you the reference you'll need either through the constructor or with a property.

Another way to do it is to store a reference to SynchronizationContext.Current in your class constructor. Use its Send or Post method. That however requires the client to have WPF initialized properly (Application.Run must have been called) and construct your class object from its UI thread.

Hans Passant
Cool, now what about Winforms too? Is there a Dispatcher object there as well?
cmw
@cmw - yes (.InvokeRequired and Invoke do the same on controls).@nobugz - Unfortunately not sure I can do that, there may be a number of controls for example label, progress bar and canvas in my sample application, passing all these into the construct and knowing which one ties in to which event will be cumbersome. I haven't tried it yet, but I'm guessing things like WebClient.DownloadAsync() will suffer similar problems to those I'm facing and require invokes?
Ian
Not a problem, there is only one Dispatcher object for all controls.
Hans Passant
Interesting, I don't know enough about the dispatcher.
Ian
+3  A: 

Use the AsyncOperationManager instead.

It will do the invoke for you. (internally it uses the SynchronizationContext as nobugz describes)

adrianm
That looks exactly like the resource I need, in fact it goes into the whole async and event based model which is essentially what I'm trying to do. Wish I knew about this resource earlier on in the design really!
Ian
Although I went down this route, I actually reverted back to my original code. I found that the SynchronizationContext.Post method was actually very slow at being able to pump the messages back to the UI.
Ian