Click here to Skip to main content
15,888,323 members
Articles / Desktop Programming / Windows Forms
Article

Prevent 'cross-thread operation not allowed' exception through reflection

Rate me:
Please Sign up or sign in to vote.
3.46/5 (13 votes)
18 Dec 2006CPOL2 min read 112.1K   384   30   29
How to avoid 'cross-thread operation not allowed' errors using reflection.

Introduction

I was often faced with a problem on using System.Windows.Forms.Control objects: we can't perform operations on them in other threads than the one they were created. The only way I found to deal with this problem is to define a delegate and use it to invoke a method on the creation thread of the control (using the ISynchronizeInvoke interface of the Control itself).

The recurring solution

I wrote and wrote and wrote pieces of code like this:

Usual solution:

VB
'The delegate method
Delegate Sub ChangeControlTextDelegate(ByVal ctrl As Control, _
                                       ByVal text As String)
'The method with the delegate signiture
Private Sub ChangeControlText(ByVal ctrl As Control, _
                              ByVal text As String) 
  If Me.InvokeRequired Then 
    Me.Invoke(New ChangeControlTextDelegate(AddressOf ChangeControlText), _
              New Object() {ctrl, text})
    Return
  End If
  ctrl.Text = text
End Sub

Writing a delegate for changing the Text property, one for changing the BackColor (great, I could reuse it for changing the ForeColor property too), and so on.

The solution using reflection

Naturally, the first step was to define my delegates once and forever in a class library so I could use them freely in forms, user defined controls, and so on, without having to redefine them. But, this approach still lacks in flexibility. So, I started to write a class which is able to take a control in its constructor and expose a method to set every non-parameterized property of the control by invoking a method with the appropriate signature, in the control's creation thread. Naturally, what such a class really needs in its Ctor isn't actually a Control, but just an object which implements the System.ComponentModel.ISynchronizeInvoke interface. I called it ThreadSafePropertySetter, and here is an example of how we can use it:

Sample code:

VB
'in a Control class body we declare a ThreadSafePropertySetter object
private threadSafePropertySetter1 as threadSafePropertySetter
'...
'in the Control's Ctor we initialize it, passing the control (usually a windows form) itself
Public Sub New()
  ' Chiamata richiesta da Progettazione Windows Form.
  InitializeComponent()
  ' Aggiungere le eventuali istruzioni di inizializzazione
  ' dopo la chiamata a InitializeComponent().
  Me.threadSafePropertySetter1 = New ThreadSafePropertySetter(Me)
End Sub
'...
'In some method of the Form or control itself
Me.ThreadSafePropertySetter1.SetCtrlProperty(Of Color)(Me.button1, "BackColor", Color.Aqua)
Me.ThreadSafePropertySetter1.SetCtrlProperty(Of String)(Me.button1, "Text", "New text")

As you can see, I use Generics to get advantage from the type safety characteristics of the .NET platform. And now, let's look at how such a ThreadSafePropertySetter is implemented.

ThreadSafePropertySetterClass

VB
'in a Control class body
Imports System.ComponentModel
Imports System.Reflection
Public Class ThreadSafePropertySetter

Delegate Sub SetCtrlPropertyDelegate(ByVal ctrl As Object, _
         ByVal propName As String, ByVal propvalue As Object)

Public Sub New(ByVal syncInvokeObject As ISynchronizeInvoke)
  Me._syncInvokeObject = syncInvokeObject
End Sub

Public Sub SetCtrlProperty(Of T)(ByVal ctrl As Object, _
       ByVal propName As String, ByVal propValue As T)
  SetObjectProperty(ctrl, propName, propValue)
End Sub

Protected Sub SetObjectProperty(ByVal obj As Object, _
          ByVal propertyName As String, ByVal propertyValue As Object)
  If _syncInvokeObject.InvokeRequired Then
    _syncInvokeObject.Invoke(New SetCtrlPropertyDelegate(AddressOf _
          SetCtrlProperty), New Object() {obj, propertyName, propertyValue})
    return
  End If
  Dim propInfo As PropertyInfo = obj.GetType.GetProperty(propertyName)
  If propInfo IsNot Nothing Then
    If propertyValue is Nothing Then
      propInfo.SetValue(obj, Nothing, Nothing)
    ElseIf propInfo.PropertyType.IsAssignableFrom(propertyValue.GetType) Then
      propInfo.SetValue(obj, propertyValue, Nothing)
    End If
  End If
End Sub

Private _syncInvokeObject As ISynchronizeInvoke

Naturally, you'll find a commented version of the class in the source code.

Further improvements

I will make the ThreasSafePropertySetter class implement the IExtenderProvider interface, and inherit the System.ComponentModel.Component class to give it Visual Studio Designer support. A disadvantage we have using the ThreasSafePropertySetter class is that we lose the IntelliSense support in setting properties: in effect, we have to pass to the SetCtrlProperty method the property name as a String, and there is a difference in setting a control's Text property this way:

VB
button1.Text="cancel"

or this way:

VB
Me.tsPropertySetter1.SetCtrlProperty(Of String)(button1, "Text", "cancel")

Any suggestions on how to retrieve the Intellisense support using the described approach?

I hope this small piece of code could be useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Jack_3219-Mar-12 7:49
Jack_3219-Mar-12 7:49 
GeneralMy vote of 5 Pin
Stephan Fourie13-Dec-11 3:36
Stephan Fourie13-Dec-11 3:36 
GeneralNice Pin
BillW3325-May-11 7:13
professionalBillW3325-May-11 7:13 
General[Message Removed] Pin
Mojtaba Vali11-May-08 0:21
Mojtaba Vali11-May-08 0:21 
GeneralRe: thanks Pin
siroman12-May-08 4:48
siroman12-May-08 4:48 
GeneralPerformance [modified] Pin
shaul_ahuva27-Dec-06 4:39
shaul_ahuva27-Dec-06 4:39 
GeneralRe: Performance Pin
TrendyTim27-Dec-06 11:37
TrendyTim27-Dec-06 11:37 
GeneralRe: Performance Pin
siroman28-Dec-06 3:05
siroman28-Dec-06 3:05 
QuestionInvoke Required use Pin
tlongman26-Dec-06 11:17
tlongman26-Dec-06 11:17 
Firstly, this approach is a very elegant and simple one that satisfies many needs, albeit it may have performance penalties in a heavy-use environment. Thank you to the author.

However, I must comment on another comment made: In attempting to do a similar thing and continually seeing people saying to check 'Invoke Required', I've not been able to successfully check 'invoke required' except on the thread that created the object. From other classes and/threads, I just don't know how to get addressability to the control that needs to be checked. Can someone explain to me why everyone says this is so easy and I haven't yet figured it out?

Thanks, Tom
Sniff | :^)
AnswerRe: Invoke Required use Pin
siroman27-Dec-06 2:24
siroman27-Dec-06 2:24 
QuestionRe: Invoke Required use Pin
tlongman28-Dec-06 12:10
tlongman28-Dec-06 12:10 
AnswerRe: Invoke Required use Pin
siroman1-Jan-07 21:11
siroman1-Jan-07 21:11 
GeneralRe: Invoke Required use Pin
tlongman2-Jan-07 17:09
tlongman2-Jan-07 17:09 
AnswerRe: Invoke Required use Pin
Filip Duyck27-Dec-06 5:08
Filip Duyck27-Dec-06 5:08 
GeneralRe: Invoke Required use Pin
tlongman28-Dec-06 12:12
tlongman28-Dec-06 12:12 
GeneralISynchronizeInvoke Pin
TrendyTim25-Dec-06 11:03
TrendyTim25-Dec-06 11:03 
GeneralRe: ISynchronizeInvoke Pin
Filip Duyck27-Dec-06 5:09
Filip Duyck27-Dec-06 5:09 
GeneralRe: ISynchronizeInvoke Pin
TrendyTim27-Dec-06 11:17
TrendyTim27-Dec-06 11:17 
GeneralRe: ISynchronizeInvoke Pin
siroman28-Dec-06 2:33
siroman28-Dec-06 2:33 
QuestionWhy ? Pin
JP van Mackelenbergh18-Dec-06 19:38
JP van Mackelenbergh18-Dec-06 19:38 
AnswerRe: Why ? [modified] Pin
siroman19-Dec-06 2:07
siroman19-Dec-06 2:07 
AnswerRe: Why ? Pin
Filip Duyck19-Dec-06 4:24
Filip Duyck19-Dec-06 4:24 
GeneralRe: Why ? Pin
siroman19-Dec-06 11:20
siroman19-Dec-06 11:20 
GeneralRe: Why ? Pin
Filip Duyck19-Dec-06 22:20
Filip Duyck19-Dec-06 22:20 
GeneralRe: Why ? Pin
siroman20-Dec-06 2:49
siroman20-Dec-06 2:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.