On Mon, 10 Nov 2008 03:24:45 -0800, Martin Groh <so*******@gmail.com>
wrote:
I'm trying to find a way to raise events from a thread or a delegate in
such a way that it will not require the use of invoke when you are
updating the gui. In other words, make the event delegate trigger form
the caller thread.
It is possible, of course. After all, this is what BackgroundWorker
does. But BackgroundWorker is a special kind of class, designed for a
very specific purpose involving exactly that. I would say that
_generally_ it is much better to have the event _subscriber_ deal with
handling the cross-thread invocation, and that will necessarily involve
the explicit use of a call to Control.Invoke() or Control.BeginInvoke() in
the event handler itself.
Today I'm doing this by finding the main form and invoking the event
delegate for there. But this method seems "to dirty" to be the best
solution.
Yes, I'd agree that if you insist on a feature like this for your own
class, that's probably one of the least-preferable ways to do it. Not the
least reason being that you have no way to know for sure that the main
form is indeed owned by the thread where the client of your class really
wants the event raised.
Depending on your needs, there are at least a couple of ways to manage
this that would be better than just picking some arbitrary form instance
to call Invoke().
Doing it the BackgroundWorker way would involve using the
SynchronizationContext class. You would have the same requirement that
BackgroundWorker does, in that the class raising the event would have to
be instantiated on the same thread where you want the event raised. In
the constructor of that class, you'd get the current thread's
synchronization context (SynchronizationContext.Current property), save
that to a private field, and then use it later to call the Post() or
Send() methods when you're raising the event.
If instead you would like to tie the thread being used to raise the event
to the class where the event handler is implemented, you can in your
event-raising code deconstruct the delegate for the event by enumerating
the invocation list (Delegate.GetInvocationList() method), and checking
each Delegate.Target reference to see if it implements
ISynchonizedInvoke. If it does, then use the ISynchronizedInvoke.Invoke()
or ISynchronizedInvoke.BeginInvoke() method to raise the event. If it
doesn't, then just raise the event normally.
Pete