Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Component Manipulator from separate threads

4.64/5 (7 votes)
26 Aug 2009CPOL1 min read 20.7K   120  
An easy to use form component manipulator from separate threads.

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:

C#
private delegate void updateFileLabel(string str, int index);

Declaring the method:

C#
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

C#
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:

C#
protected delegate object GetComponentPropertyDelegate(Control component, string query);

To use this method, you write:

C#
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

C#
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:

C#
protected delegate void SetComponentPropertyDelegate(Control component, 
                        string query, object value);

To use this method, you write:

C#
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

C#
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:

C#
protected delegate void CallComponentMethodDelegate(Control component, 
                                  string query, object [] parameters);

To use this method, you write:

C#
ComponentManipulator.CallComponentMethod(listView1, 
         "Items[" + item + "].SubItems[1].ResetStyle");

Note: The method must not have parentheses ().

License

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