Click here to Skip to main content
15,889,462 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.3K   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

 
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 
GeneralRe: Why ? Pin
Filip Duyck27-Dec-06 5:03
Filip Duyck27-Dec-06 5:03 
GeneralRe: Why ? Pin
siroman27-Dec-06 5:33
siroman27-Dec-06 5:33 
GeneralRe: Why ? Pin
Filip Duyck27-Dec-06 5:40
Filip Duyck27-Dec-06 5:40 
siroman wrote:
Effectively I post articles, read articles, read books, write code with the pourpose to learn "my" language. Surely my intention is not to teach you something

If that is the case, then perhaps you should not post at CodeProject. I am not sure if you are aware, but this isn't exactly a "forum". This is a place where other newbies like you (no offense intended, everybody starts somewhere) go to get answers, and they read your post, and think you do have the answers.

The obvious problems with your code and why it is not good has been laid out in other comments. I am sorry if I offend you, but I am not sure why you expect me to give you a pat on the back and thumbs up if you present code that is under par.

And just to be sure, I'm not angry with you. I don't have any reasons to be.
GeneralRe: Why ? Pin
siroman27-Dec-06 22:48
siroman27-Dec-06 22:48 

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.