Sunday, December 27, 2009

My Solution: Cross-thread operation not valid error while setting a value to control through property


Last week I was working on a code in which a control's property was being accessed from a thread. Obviously as it was a multithreaded application, it should throw the "Cross-thread operation not valid" error. I know that if we are accessing any method from a different thread, we need to use the delegates and events. But my concern was how I can do the same with property as there are no delegates associated with properties of a control.

I created one simple VS 2008 application which set the text property of a text box on a different thread, when I ran the program, I got the following exception.

"Cross-thread operation not valid: Control 'textBox' accessed from a thread other than the thread it was created on."

To tackle this I was thinking about a way which is similar to the techniques we use while handling the same error for methods i.e. make use of events and delegates.

So I created a simple delegate and event as follows

private delegate void SetControlValue(Control controlType,
string propertyName, object value);
private event SetControlValue SetControlValueEvent = null;


And wrote a new method as follows

private void SetControlValueMethod(Control controlType,
string propertyName, object value)

{ 
if (this.InvokeRequired)
{
this.Invoke(new SetControlValue(SetControlValueMethod),new object[]   

{controlType, propertyName, value });
}
else 

{
        PropertyInfo[] props = controlType.GetType().GetProperties(); 

foreach (PropertyInfo pi in props)
{
    if (pi.Name.ToUpper().Trim() == propertyName.ToUpper().Trim())
       {
            pi.SetValue(controlType, value, null);
       }
}
}
}


Here I have used reflection to get the PropertyInfo and used the Setvalue method to assign the appropriate value to the control.

And I used instead of calling

textBox.Text = _number.ToString();

I called the new method to set the values

SetControlValueMethod(textBox, "Text", _number.ToString());


This need to modify to add intellisense to the method so that the control type and their respective properties should come automatically and any error that may cause due to typo can be avoided.

1 comment:

  1. Better solution:

    public static void Invoke(this Control me, Action invoker)
    {
    if (me.InvokeRequired)
    {
    me.Invoke(invoker);
    }
    else
    {
    invoker();
    }
    }

    Then:

    textBox.Invoke(() => textBox.Text = _number.ToString());

    Full intellisense, much less scaffolding code, no reflection (and thus faster), and more general---it's not just for setting values; you can do anything inside the lambda.

    ReplyDelete