ediblecode
ediblecode

Reputation: 11971

Thread Pooling: Cross-thread operation not valid.

I am quite new when it comes to threading but I am getting an InvalidOperationException when using the following code. I understand that it is trying to access importFileGridView but this was created by the UI thread which creates the exception. My question is, how do I resolve this? Is it possible for GetAllImports to have a return type? How do I access temp from my UI thread?

ThreadPool.QueueUserWorkItem(new WaitCallback(GetAllImports), null);

private void GetAllImports(object x)
    {
        DataSet temp = EngineBllUtility.GetAllImportFiles(connectionString);
        if (temp != null)
            importFileGridView.DataSource = temp.Tables[0];
        else
            MessageBox.Show("There were no results. Please try a different search", "Unsuccessful", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

Upvotes: 5

Views: 1424

Answers (2)

albertjan
albertjan

Reputation: 7817

What reed said but I like this syntax a bit better:

What happens is you're creating a delegate function which will be passed as a parameter to the UI thread via Control.Invoke which Invokes it, this way the UI thread makes the changes to the importFileGridView.

importFileGridView.Invoke((MethodInvoker) delegate {
                             importFileGridView.DataSource = temp.Tables[0];
                         });

You could also write this like this:

//create a delegate with the function signature
public delegate void SetDateSourceForGridViewDelegate (GridView gridView, Object dataSource);

//write a function that will change the ui
public void SetDataSourceForGridView(GridView gridView, Object dataSource)
{
    gridView.DataSource = dataSource;
}

//Create a variable that will hold the function
SetDateSourceForGridViewDelegate delegateToInvoke = SetDataSourceForGridView;

//tell the ui to invoke the method stored in the value with the given paramters.
importFileGridView.Invoke(delegateToInvoke, importFileGridView, temp.Tables[0]);

and I'd advice the use of MethodInvoker over Action see: here

Upvotes: 0

Reed Copsey
Reed Copsey

Reputation: 564413

You can't change a user interface component on a background thread. In this case, setting the DataSource must be done on the UI thread.

You can handle this via Control.Invoke or Control.BeginInvoke, like so:

private void GetAllImports(object x)
{
    DataSet temp = EngineBllUtility.GetAllImportFiles(connectionString);
    if (temp != null)
    {
        // Use Control.Invoke to push this onto the UI thread
        importFileGridView.Invoke((Action) 
            () => 
            {
                importFileGridView.DataSource = temp.Tables[0];
            });
    }
    else
        MessageBox.Show("There were no results. Please try a different search", "Unsuccessful", MessageBoxButtons.OK, MessageBoxIcon.Information);
}

Upvotes: 3

Related Questions