views:

1995

answers:

3

I have become painfully aware of just how often one needs to write the following code pattern in event-driven GUI code, where

private void DoGUISwitch() {

    // cruisin for a bruisin' through exception city
    object1.Visible = true;
    object2.Visible = false;

}

becomes

private void DoGUISwitch() {

    if (object1.InvokeRequired) {

        object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));

    } else {

        object1.Visible = true;
        object2.Visible = false;

    }
}

This is an awkward pattern in C#, both to remember, and to type. Has anyone come up with some sort of shortcut or construct that automates this to a degree? It'd be cool if there was a way to attach a function to objects that does this check without having to go through all this extra work, like a object1.InvokeIfNecessary.visible = true type shortcut.

Previous answers have discussed the impracticality of just calling Invoke() every time, and even then the Invoke() syntax is both inefficient and still awkward to deal with.

So, has anyone figured out any shortcuts?

+17  A: 

You could write an extension method:

public static void InvokeIfRequired(this Control c, Action<Control> action)
{
    if(c.InvokeRequired)
    {
        c.Invoke(() => action(c));
    }
    else
    {
        action(c);
    }
}

And use it like this:

object1.InvokeIfRequired(c => { c.Visible = true; });

Lee
That's a nice trick. I'm definitely stealing it.
Jonathan Allen
Very nice indeed.
Ian
A: 

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; });
Dan Tao
What do you mean by 'better way of doing things'? The goal is to keep the GUI responsive and the user informed during various operations, which naturally have to occur in other threads?
Tom the Junglist
@Tom: Often I find that developers get in the habit of combining UI update logic with the actual business logic of the application itself, which can work fine until you start introducing threads, at which point it gradually becomes obvious that the two should have been separate to begin with. In the main application we use at my company, for instance, all of the "critical" work done by the software takes place on non-UI threads, while the UI thread updates itself on timers twice a second.
Dan Tao
@Tom: On the other hand, if you have some long-running process executing on a separate thread and you simply want to update the UI according to the status of the background process, that's precisely what the `BackgroundWorker` control's `ReportProgress` method (and corresponding `ProgressChanged` event) is for. So again, calling `Control.Invoke` remains unnecessary.
Dan Tao
I haven't used BackgroundWorker much, partially because the whole 'dropping logical objects on the form' is a bit cheesy, but mostly because I've been content writing my own events with stuff run through QueueUserWorkItem. One of my reasons that this whole thing came up was because the event handlers always seem to run off a different thread anyway, which meant all this tedious InvokeRequired jazz. Unfortunately my use cases are not simple, one actual instance required action once a PictureBox as finished LoadAsync(), which actually introduced me to some wonderful GDI+ bugs too :-/
Tom the Junglist
I also wanted to add that I work in a variety of non-MS non-visual tech, so my knowledge and style of C# generally favors things that are analogous to other languages
Tom the Junglist
This is more complicated than the original way to do things...
Tim
@Tim: It depends on what you want to accomplish. Implementing it, yes, requires a lot of work. Once it's in place, though, utilizing the resulting code is extremely straightforward. As it turns out, [I've written a library](http://threadsafecontrols.codeplex.com/) that does basically exactly what I've described here, and personally I think it is quite useful and easy to use. Whether it's worth your time or not -- that's for you to decide.
Dan Tao
+6  A: 

Here's the form I've been using in all my code.

private void DoGUISwitch()
{ 
    Invoke( ( MethodInvoker ) delegate {
        object1.Visible = true;
        object2.Visible = false;
    }
} 

I've based this on the blog entry here. I have not had this approach fail me, so I see no reason to complicate my code with a check of the InvokeRequired property.

Hope this helps.

Matt Davis
+1 - I stumbled on the the same blog entry you did, and think this is the cleanest approach of any proposed
Tom Bushell