A better call to Control.Invoke

by Miha Markič 26. June 2009 17:20

Whenever one needs to interact with WinForms UI from within another thread (the one that didn't create the UI controls) one has to rely on the Control.Invoke (or BeginInvoke/EndInvoke pair) method. Otherwise one gets an InvalidOperationException exception with message “Cross-thread operation not valid: Control ‘name here’ accessed from a thread other than the thread it was created on”. This happens because WinForms (and .net framework in general) isn’t thread safe.

Let’s say I want to know how many controls are on the form. I’d do it like this (Form f holds the instance of the Form created in some thread):

public static int GetControlsCount(Form f)
{
if (f.InvokeRequired)
return (int)f.Invoke(new Func<Form, int>(GetControlsCount), f);
else
return f.Controls.Count;
}
...
int count = GetControlsCount(f);

This way the method GetControlsCount is thread safe. That’s fine. But what should I do if I require to access many methods in such a thread safe way (I’ll blog about the reason I need it in a future post)? Creating this kind of wrapper for every different method I need to call would be kind of boring at best and difficult to maintain, don’t you think?

So I created a generic method instead, like this:

public static TRet InvokeSync<TRet>(Control control, Func<TRet> func)
{
if (control.InvokeRequired)
return (TRet)control.Invoke(func);
else
return func();
}
...
int count = InvokeSync<int>(f, () => f.Controls.Count);

This is much better because I don’t need to create a wrapper for each method – instead I merely provide a delegate (of type Func<int> in my case). There are two drawbacks to this approach.

1. It won’t work with different number of parameters nor it will work with methods (= actions) that don’t return a value. The only workaround is to create various similar methods that accepts different argument number and return either a result or no result (void). Here is a bunch of such variations:

public static void InvokeSync(Control control, Action action)
{
if (control.InvokeRequired)
control.Invoke(action);
else
action();
}
public static void InvokeSync<T>(Control control, Action<T> action, T argument)
{
if (control.InvokeRequired)
control.Invoke(action, argument);
else
action(argument);
}
public static TRet InvokeSync<T, TRet>(Control control, Func<T, TRet> func, T argument)
{
if (control.InvokeRequired)
return (TRet)control.Invoke(func, argument);
else
return func(argument);
}
public static TRet InvokeSync<T1, T2, TRet>(Control control, Func<T1, T2, TRet> func, T1 argument1, T2 argument2)
{
if (control.InvokeRequired)
return (TRet)control.Invoke(func, argument1, argument2);
else
return func(argument1, argument2);
}

2. Passing Control parameter (which is required to do the synchronization) is such pre .net 3.5-ish. Instead extensions methods can be used, like this:

public static class Extender
{
public static TRet InvokeSync<TRet>(this Control c, Func<TRet> func)
{
if (c.InvokeRequired)
return (TRet)c.Invoke(func);
else
return func();
}

public static void InvokeSync(this Control c, Action func)
{
if (c.InvokeRequired)
c.Invoke(func);
else
func();
}
}

So the final version of the call to InvokeSync looks even nicer:

int count = f.InvokeSync<int>(() => f.Controls.Count);

That’s it.

InvokeSyncExtender.zip (572.00 bytes)

Tags: , ,

.net 3.5 | VS 2008

Comments (6) -

Miha Markic
Miha Markic Slovenia
6/30/2009 5:22:35 AM #

Hi Igor,

No, I didn't know about that article until now - thanks for the link.
However, I don't like the approach very much because:
- I don't want to modify host code (the original code). That's my specific requirement
- Doing it that way it always synchroinizes with UI which might be not a best thing to do (what if event listener doesn't require UI synchronization or ever if there is no listener at all)
Not that mentioned approach is bad or anything (I am sure it is fine for some, but no all, situations) - it doesn't fit my situation.

Reply

Renaud B&#233;dard
Renaud Bédard Canada
7/13/2009 9:19:51 PM #

Nice stuff, I'll definitely be using this instead of Begin/EndInvoke pairs!

Reply

Back Pain
Back Pain United States
11/18/2009 3:35:01 PM #

I don't like the approach very much because:
I don't want to modify host code (the original code). That's my specific requirement
Doing it that way it always synchroinizes with UI which might be not a best thing to do

Reply

hemorr&#243;idas
hemorróidas United States
11/24/2009 1:51:32 PM #

It won’t work with different number of parameters nor it will work with methods (= actions) that don’t return a value.

Reply

Miha Markic
Miha Markic Slovenia
11/24/2009 9:56:03 PM #

You have to add overloads for both cases (void return and increased count of parameters). It isn't a big deal though as there aren't that many parameters one usually uses (look at Action<> or Func<> definition).
Plus one can create a template for code generation that would create as many overloads as one wants.

Reply

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

Miha Markic

About me
Righthand
 
Microsoft MVP
 
Developer Express' DXSquad
INETA Country Leader for Slovenia
INETA Country Leader for Slovenia

Slovene Developer Users Group Lead
Friends of Red-Gate
LLBLGenPro Partner

Miha currently works as a free lance consultant and software developer specialized in .net area.
He graduated in Computer and information science at the University of Ljubljana, Slovenia. He has accumulated experience in various programming languages such as Java, Visual Basic 3-6 (MCP), Visual C++, Delphi, C# and VB.Net through years.
He has experience in practically all (technical) stages of project development, including planning, framework development, user interface, business processes, as well as testing and documenting. He has worked on big and small projects in Slovenia and abroad (e.g. participated in completing level 3 IS for the Nucor steel plant, Hertford, USA).
Currently he enjoys programming in .net environment using C#. Since 2000 he has been active in Developer Express' DX Squad and has been ECDL trainer and tester. He also gives lectures on conferences and other events in Slovenia.

Month List

Tag cloud

Most comments

Paulius Paulius
1 comments
us United States
Meh Meh
1 comments
us United States
bart dm bart dm
1 comments
nl Netherlands

RecentComments

Comment RSS