UPDATE:
I'm hesitant to bring this up, as it's not something that I consider 100% ready for public release*, but...
I actually wrote a library that does pretty much what I describe below, and it seems to work pretty well: ThreadSafeControls.
The idea behind the project is basically that, instead of checking InvokeRequired
everywhere, or even calling some helper method like InvokeIfRequired
, all you do for any given control is get a thread-safe wrapper for it using the API and then you can access it programmatically just like a regular control.
In other words, instead of code like this:
Using InvokeRequired
(Okay)
void UpdateOnBackgroundThread()
{
if (InvokeRequired)
{
BeginInvoke(new Action(UpdateOnBackgroundThread));
}
else
{
TextBox tbx = myTextBox;
tbx.Visible = true;
tbx.Text = "Work
}
}
...or even like this:
Using a helper method (e.g., InvokeIfRequired
) (Better)
void UpdateOnBackgroundThread()
{
TextBox tbx = myTextBox;
tbx.InvokeIfRequired(t => t.Visible = true);
tbx.InvokeIfRequired(t => t.Text = "Hello");
}
...your code can work just like this:
Using ThreadSafeControls
(Best - in my opinion)
void UpdateOnBackgroundThread()
{
ThreadSafeTextBox tbx = myTextBox.AsThreadSafe();
tbx.Visible = true;
tbx.Text = "Hello";
}
I've tested this library and it works well for simple scenarios. I haven't written a thread-safe version of every control under the sun, of course (the most glaringly absent functionality is any kind of thread-safe code for scenarios involving data binding), nor have I stress-tested the library in any particularly comprehensive way. It is what it is.
To any interested developers who'd like to try it out and give me feedback: that would be very appreciated! I haven't worked on it in a little while, but I do fully intend to come back to it and polish it up at some point in the near future. That is unless somebody comes along and convinces me it was a huge waste of time to begin with ;)
Anyway, I only made this update because I recently got a downvote on this answer, and I feel that this idea has some potential. Also, this library has been around for a few months now and I haven't really told anyone about it; if I'm ever going to get around to improving it, having people out there actually using it seems like one effective way to make that happen.
*Basically, it's published because CodePlex forces you to publish a project within 30 days of creating it, and I had created it on CodePlex so that I could use Mercurial.
EDIT: Actually, a little snippet of your question caused an idea to pop into my head.
This would require a decent amount of setup, but it could allow for some really neat, intuitively thread-safe code.
The basic idea would be to start by writing a wrapper class for Control
. This class would provide all the same properties/methods of the underlying control type that you wanted to access in a thread-safe manner:
As a quick example, the below class would provide a thread-safe Visible
property*.
public class SyncControl<TControl> where TControl : Control
{
protected TControl _ctrl;
public bool Visible
{
get { return _ctrl.Visible; }
set { UpdateSync(() => _ctrl.Visible = value); }
}
public SyncControl(TControl ctrl)
{
if (ctrl == null)
throw new ArgumentNullException("ctrl");
_ctrl = ctrl;
}
protected void UpdateSync(Action action)
{
if (_ctrl.InvokeRequired)
{
_ctrl.Invoke(action);
}
else
{
action();
}
}
}
Then write an extension method to provide an instance of this wrapper class for a given control:
public static SyncControl<TControl> AsThreadSafe<TControl>(this TControl control) where TControl : Control
{
return new SyncControl<TControl>(control);
}
Then you could write code just like this:
myButton.AsThreadSafe().Visible = true;
Obviously you could implement whatever properties/methods in addition to Visible
that you desire. You could (and would) obviously also subclass SyncControl<TControl>
for controls with additional properties, like this:
public class SyncTextBox : SyncControl<TextBox>
{
public string Text
{
get { return _ctrl.Text; }
set { UpdateSync(() => _ctrl.Text = value); }
}
public SyncTextBox(TextBox tbx) : base(tbx) { }
public void Clear()
{
UpdateSync(() => _ctrl.Clear());
}
}
And the extension:
public static SyncTextBox AsThreadSafe(this TextBox tbx)
{
return new SyncTextBox(tbx);
}
Example code:
myTextBox.AsThreadSafe().Text = "Hello world.";
What do you think?
* Disclaimer: I honestly can't remember if accessing properties of Windows Forms controls requires invoking as well. If so, obviously, you'd have to make the property getters thread-safe as well.
Original answer:
Well, honestly, I find that often when you run into code like this it's a sign that there's probably a better way of doing things.
That said, a quick & dirty approach I've been guilty of using before is something like this (copying from my memory):
public static UpdateSync(Control ctrl, Action action) {
if (ctrl.InvokeRequired) {
ctrl.Invoke(action);
} else {
action();
}
}
You can call this like so:
UpdateSync(object1, () => { object1.Visible = true; object2.Visible = false; });