QUESTION: In .NET 3.5 WinForms apps, how do I access/call methods in UI thread from a separate thread, without passing a delegate?
EXAMPLE: Say I have some code I want to run both (a) manually when the user clicks a button, and (b) periodically called by a process which is running in a separate non-mainUI thread but without passing a delegate. [Simplistically I'm thinking that the class that has this method is already been constructed, and the main UI thread has a handle to it, therefore if the process running in the separate thread could just get a handle to it from the main-UI thread it could call it. Hopefully this is not a flawed concept]
BACKGROUND: I'm actually after a way to do the above for the case where my separate process thread is actually a job I schedule using quartz.net. The way the scheduler works I can't seem to actually pass in a delegate. There is a way to pass JobDetails, however it only seems to caters for things like string, int, etc. Hence what I'm after is a way to access the MainForm class for example, to call a method on it, from within the quartz.net job which runs in a separate thread.
Thanks
.Net doesn't allow fiddling with the UI from a non-UI thread; There are ways around it such as
Invoke
, but this is the only way to (safely) do it without resorting to polling a shared object.You could try the BackgroundWorker control in the toolbox, this works for simple things.
Assuming you have access to the MainForm, you can simply call any of it's methods, but those methods will then bear the burden of checking if they need to be marshalled to the UI thread and handle the delegate passing there.
So on your main form you could have a method:
This is one approach for a WinForms app, if it has a form "MainForm" that always exists once the app is initialized. I cache a reference to that in a static variable, and then have a static helper method that I use in all methods that need access to UI, and might be called from non-UI threads.
What I like about this approach, is that after the initial setup, you can write UI-touching code in ANY class, not just classes that are controls. And the coding is a simple matter of wrapping an action inside a call to
MyApp.RunOnUIThread
. See the definitions forSomeUIWork1
,SomeUIWork2
, andSomeUIWork3
for variations on this.Limitations and Caveats:
If your app does not have a form that always exists, or for some reason you have MULTIPLE UI threads, then this solution will need to be adapted, or may not be useful to you.
This approach, like all approaches involving RequireInvoke or similar tests, can be OVERUSED, resulting in a hard-to-maintain/understand system. It is recommended only as a last resort. (I used it when enhancing legacy code, where it was too much development time to safely refactor the existing code.)
If practical, instead of doing what I do here, separate your UI code from non-UI code. For example, use BackgroundWorker with progressChanged https://stackoverflow.com/a/10646636/199364.
In C#:
In VB: