5

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);
    }
ediblecode
  • 11,701
  • 19
  • 68
  • 116

2 Answers2

3

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);
}
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Normally when I see this code, it will typically check if the Invoke is actually required before doing it (with `.InvokeRequired`). Is there something special about this situation that makes it unnecessary? – M.Babcock Dec 29 '11 at 16:06
  • @M.Babcock Since this is done directly in a call from `ThreadPool.QueueUserWorkItem`, you know, in advance, that you're on a ThreadPool thread. This will mean that InvokeRequired will ALWAYS return true. Putting the check becomes an unnecessary check (in this specific case). If you're writing a method that can be used from both the UI thread and other threads, however, then it is a good idea to add the check. – Reed Copsey Dec 29 '11 at 16:10
  • @ReedCopsey Forgive me, but I don't understand how to implement this? – ediblecode Dec 29 '11 at 16:15
  • @user1016253 You can just replace your code with the code above, and it should fix the problem... – Reed Copsey Dec 29 '11 at 16:16
  • @user1016253 What error are you seeing? What version of C#/Visual Studio are you using? – Reed Copsey Dec 29 '11 at 16:42
  • @ReedCopsey: What you're wanting to do is get the GUI thread to run the code. GUI threads automatically have a "Dispatcher". What the dispatcher mostly does is runs delegates. Calling the Invoke() method of an object will queue the delegate you pass in into the dispatcher of the parent thread. This means calling invoke against your grid will queue a delegate on the proper thread to run your update code. – Bengie Dec 29 '11 at 17:13
0

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

Community
  • 1
  • 1
albertjan
  • 7,739
  • 6
  • 44
  • 74