Introduction
When using multiple threads in C# and you want to modify a control on the form, you receive an error like this:
Cross-thread operation not valid: Control 'XXX' accessed from
a thread other than the thread it was created on.
To resolve this problem, you have to create a delegate for each component you want to modify, or, if you want to separately modify more properties, more delegates. If you want to modify many components, you end up creating a lot of delegates and delegate methods.
Background
Creating a simple delegate
Declaring the delegate:
private delegate void updateFileLabel(string str, int index);
Declaring the method:
private void updateLabel(string str, int index)
{
if (label3.InvokeRequired)
{
updateFileLabel d = new updateFileLabel(updateLabel);
this.Invoke(d, new object[] { str, index });
}
else
{
label3.Text = "Proccessing: " +
str.Substring(0, Math.Min(5,str.LastIndexOf("\\"))) +
"..." + str.Substring(str.LastIndexOf("\\"));
}
}
And if you want to manipulate many components and more than one property, there is a lot of code to write. The solution: Component Manipulator. To access the different properties, the class uses GetType()
that is defined for any object, and from there, using Reflection, everything is simple.
Using the code
The use of the code is simple. All the methods are static. There are three different methods to manipulate the components.
GetComponentProperty
public static object GetComponentProperty(Control component, string query)
{
if (component == null) { return null; }
if (component.InvokeRequired)
{
GetComponentPropertyDelegate delegateObject =
new GetComponentPropertyDelegate(GetComponentProperty);
return component.Invoke(delegateObject, new object[] { component, query });
}
else
{
object obj = GetQueryObject(component, ref query);
Property property = GetProperty(query);
obj = GetObjectPropertyValue(obj, property.name);
if (obj == null) { return null; }
if (property.index != null)
{
obj = GetItemFromArray(obj, property.index);
}
return obj;
}
}
This method uses a delegate:
protected delegate object GetComponentPropertyDelegate(Control component, string query);
To use this method, you write:
bool isChecked = (bool)ComponentManipulator.GetComponentProperty(listView1,
"Items[" + item + "].Checked");
string fileName = ComponentManipulator.GetComponentProperty(listView1,
"Items[" + item + "].Text") as string;
The query
parameter is used to access the property you want.
SetComponentProperty
public static void SetComponentProperty(Control component, string query, object value)
{
if (component == null || value == null) { return; }
if (component.InvokeRequired)
{
SetComponentPropertyDelegate delegateObject =
new SetComponentPropertyDelegate(SetComponentProperty);
component.Invoke(delegateObject, new object[] { component, query, value });
}
else
{
object obj = GetQueryObject(component, ref query);
Property property = GetProperty(query);
if (property.index != null)
{
obj = GetObjectPropertyValue(obj, property.name);
if (obj == null) { return; }
SetItemFromArray(obj, property.index, value);
}
else
{
SetObjectPropertyValue(obj, query, value);
}
}
}
This method uses a delegate:
protected delegate void SetComponentPropertyDelegate(Control component,
string query, object value);
To use this method, you write:
ComponentManipulator.SetComponentProperty(listView1,
"Items[" + item + "].SubItems[1].Text",
"Not selected for encoding");
The query
parameter is used to access the property you want. The value
parameter is the value to set for the property you access with the query.
CallComponentMethod
public static void CallComponentMethod(Control component, string query, object [] parameters)
{
if (component == null) { return; }
if (component.InvokeRequired)
{
CallComponentMethodDelegate delegateObject =
new CallComponentMethodDelegate(CallComponentMethod);
component.Invoke(delegateObject, new object[] { component, query });
}
else
{
object obj = GetQueryObject(component, ref query);
Property property = GetProperty(query);
if (property.index == null)
{
CallObjectMethod(obj, query, parameters);
}
}
}
This method uses a delegate:
protected delegate void CallComponentMethodDelegate(Control component,
string query, object [] parameters);
To use this method, you write:
ComponentManipulator.CallComponentMethod(listView1,
"Items[" + item + "].SubItems[1].ResetStyle");
Note: The method must not have parentheses ().