I currently trying to write a component where some parts of it should run on the UI thread (explanation would be to long). So the easiest way would be to pass a control to it, and use InvokeRequired/Invoke on it. But I don't think that it is a good design to pass a control reference to a "data/background"-component, so I'm searching for a way to run code on the UI thread without the need of having a control available. Something like Application.Dispatcher.Invoke in WPF...
any ideas, thx Martin
There's a better, more abstract way to do this that works on both WinForms and WPF:
This works because WindowsForms installs a
WindowsFormsSynchronizationContext
object as the current sync context. WPF does something similar, installing it's own specialized synchronization context (DispatcherSynchronizationContext
)..Post
corresponds tocontrol.BeginInvoke
, and.Send
corresponds tocontrol.Invoke
.You are right, it is not good to pass controls to threads. Winforms controls are single-threaded, passing them to multiple threads can cause race conditions or break your UI. Instead, you should make your thread's features available to the UI and let it call the thread when the UI is good and ready. If you want to have background threads trigger UI changes, expose a background event and subscribe to it from the UI. The thread can fire off events whenever it wants and the UI can respond to them when it is able to.
Creating this bidirectional communication between threads that does not block the UI thread is a lot of work. Here is a highly abbreviated example using a BackgroundWorker class:
First, in your form constructor, keep a class-scoped reference to the
SynchronizationContext.Current
object (which is in fact aWindowsFormsSynchronizationContext
).Then, anywhere within your class, use this context to send messages to the UI:
You will need the following extension methods to work with lambda expressions: http://codepaste.net/zje4k6
Put the UI manipulation in a method on the form to be manipulated and pass a delegate to the code that runs on the background thread, à la APM. You don't have to use
params object p
, you can strongly type it to suit your own purposes. This is just a simple generic sample.This approach is predicated on the fact that a delegate refers to a method on a particular instance; by making the implementation a method of the form, you bring the form into scope as
this
. The following is semantically identical.What about passing a System.ComponentModel.ISynchronizeInvoke? That way you can avoid passing a Control.