I Code, Therefore I Am

while(!(succeed == try()));

Extension Methods – Handling InvokeRequired

Extension Methods can come in very handy, because in a lot of cases whatever you’re working with doesn’t have the exact functionality you’re looking for, and an Extension Method allows you to add this functionality (or extend it) without having to create a new class inheriting from said object.

One extension method I wrote some time ago happens to be one that I use the most. It’s an extensions method that takes care of controls InvokeRequired property, which helps prevent a cross-thread exception when using multiple threads in an application, God knows I’ve enjoyed enough of those in my programming career.

I will show how this extension method looks in both C# and VB.NET, and how you would use it in both languages. This extension method is generic, meaning it can be used for any control that you can think of, and it utilizes ISynchronizeInvoke, which gives us a synchronously or asynchronously execute a delegate. You provide it with a control and an Action Delegate for it to execute.

NOTE: In VB.NET an Extension method can only be created in a Module and must be marked with the ExtensionAttribute class. Here’s how our Extension method looks in VB.NET

Imports System.ComponentModel

Module InvokeRequiredHandler
    <System.Runtime.CompilerServices.Extension()> _
    Public Sub HandleInvokeRequired(Of T As ISynchronizeInvoke)(ByVal controlToInvoke As T, ByVal actionToPerform As Action(Of T))
        'Check to see if the control's InvokeRequired property is true
        If controlToInvoke.InvokeRequired Then
            'Use Invoke() to invoke your action
            controlToInvoke.Invoke(actionToPerform, New Object() {controlToInvoke})
        Else
            'Perform the action
            actionToPerform(controlToInvoke)
        End If
    End Sub
End Module

And this is how it looks in C#

using System.ComponentModel;

namespace ExtensionMethods.Example.Core
{
    public static class SynchronizeInvoke
    {
        public static void CustomInvoke<T>(this T @controlToInvoke, Action<T> actionToPerform) where T : ISynchronizeInvoke
        {
            if (@controlToInvoke.InvokeRequired)
                @controlToInvoke.Invoke(actionToPerform, new object[] { @controlToInvoke });
            else
                actionToPerform(@controlToInvoke);
        }
    }
}

Now that it’s an Extension method it will show up in Intellisense in Visual Studio for our controls. When using it in VB.NET we would use it like so (In this example I’m using a ListView class to add items to it from within a BackgroundWorker, remember this is just an example and it will work for all the controls):

ListViewControl.HandleInvokeRequired(Function(l As ListView) l.Items.Add(lv))

When in C# we can use a Lambda operator and the call looks like this:

ListViewControl.HandleInvokeRequired(lv => lv.Items.Add(lv))

Both languages use a Lambda Expression, but VB.NET uses the format Function(parameter) while C# allows for the use of the lambda operator. I think the C# method is easier to read and more intuitive but that’s just me. Some say the lambda operator is too cryptic but that’s a discussion for another time. So that’s our InvokeRequired extension method, hope you found this useful and thanks for reading

Happy Programming!


About The Author

Richard has been a professional software developer for over 15 years now. In fact he was a geek long before being a geek was 'cool'. He has his Bachelors in Computer Science from The University of Georgia and is an avid, passionate .NET developer. Richard eats, sleeps, drinks and dreams in code

Comments

5 Responses to “Extension Methods – Handling InvokeRequired”

  1. Richard says:

    Richard,

    I was able to get this figured out. The HandleInvokeRequired method for VB.NET as described above is not returning a value. As such you need to use the following:

    recState.HandleInvokeRequired(Sub(recState) recState.Text = sStatus)
    Act_Start.HandleInvokeRequired(Sub(Act_Start) Act_Start.Enabled = False)
    Act_Stop.HandleInvokeRequired(Sub(Act_Stop) Act_Stop.Enabled = False)

    After I did this, started working like a treat. Once again, thanks for your great blog on this subject.

    -Richard

  2. Richard Fouts says:

    Richard,

    Great article, however I am at a loss here. I am using your code (VS2010, .NET 3.5 Project) and when I use the code, the controls are not behaving as expected.

    In these lines:

    recState.HandleInvokeRequired(Function(recState) recState.Text = sStatus)
    Act_Start.HandleInvokeRequired(Function(Act_Start) Act_Start.Enabled = False)
    Act_Stop.HandleInvokeRequired(Function(Act_Stop) Act_Stop.Enabled = False)

    The form’s label is never updated with the data from sStatus and the buttons are not switched to be disabled or enabled. The above is without the ‘As TypeOf’, I have tried it with the As TypeOf [As Label and As Button] respectively but the same thing occurs, nothing happens. Am I just not understanding the concept???

    Thanks for any help,

    -Richard

  3. Dreed says:

    On my test project I was using version 4

  4. Richard McCutchen says:

    It’s weird that this happened to you, the code I offered is straight from many applications I’ve created in the past (and one I’m currently working on). What version of the Framework are you using?

  5. Dreed says:

    Great, many thanks for this! But one question. I got your code working like this.

    textBox1.CustomInvoke(delegate(TextBox t)
    {
          t.Text = "Hello from Thread";
    });
    

    Why is TextBox t required?

    I made some changes to your code, like this

    public static void CustomInvoke(this T ControlToInvoke, Action ActionToPerform) where T : ISynchronizeInvoke
    {
          if (ControlToInvoke.InvokeRequired)
                ControlToInvoke.Invoke(ActionToPerform, null);
          else
                ActionToPerform();
    }
    

    And in use

    textBox1.CustomInvoke(delegate()
    {
          textBox1.Text = "Hello from Thread";
    });
    

    Is there performance/security to gain from using your code or it doesn’t really matter?
    Once again thanks for this extension (I hope the code in this comment doesn’t get messed up now).

Leave a Reply