1

I've got my main form Form1 running the main bulk of my program.

I have a separate thread started to perform an algorithm.

When I run the method from the new thread, method MyAlgorithm() I get the error

InvalidOperationException with the message, "Control control name accessed from a thread other than the thread it was created on."

How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?

This is the method that I want to run contained in Form1, the main class in my application.

// Reset the results values
public void ShowResults()
{
    while (true)
    {

        loopsNum.Text = Convert.ToString(resultLoopsNum);
        nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
        nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
        cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
        shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);

    } 
}

I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.

I'm invoking my thread like this...

        // Set the algorithm method up in it's own thread
        Thread thread = new Thread(new ThreadStart(MyAlgorithm));

        // Run the algorithm
        thread.Start();
Luke
  • 22,826
  • 31
  • 110
  • 193
  • possible duplicate of [Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on](http://stackoverflow.com/questions/142003/cross-thread-operation-not-valid-control-accessed-from-a-thread-other-than-the) – Brian Gideon Mar 22 '12 at 17:47

4 Answers4

5

How do I get back to the original thread so that I can run the method to update my text boxes with the latest values?

In Windows Forms, you'd either use Control.Invoke/BeginInvoke or use a BackgroundWorker and perform the update in the progress event handler.

In WPF you'd use Dispatcher.Invoke/BeginInvoke.

In C# 5 and .NET 4.5 you'll be able to use async methods which should make a lot of this much simpler...

I've looked at the Invoke() methods, but I don't know how to get the original instance of my Form1 from the threaded method.

If the "threaded method" is just an instance method of the Form, then you've already got the this reference. If it's not, you'll need to provide that information - ideally as an ISynchronizeInvoke to avoid a direct dependency on Windows Forms if you can express the "update" part separately. (That interface is somewhat deprecated these days, in favour of synchronization contexts, but it still works perfectly well.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

Have a look at Control.Invoke():

public void ShowResults()
{
  while (true)
  {
    Thread.Sleep(1000); // don't spam the UI thread

    if (this.InvokeRequired) 
    {
      this.Invoke((Action)UpdateGui);
    }  
    else
    {
      UpdateGui();
    }
  } 
}

private void UpdateGui() 
{  
  loopsNum.Text = Convert.ToString(resultLoopsNum);
  nodesVisitedNum.Text = Convert.ToString(resultNodesVisitedNum);
  nodesResolvedNum.Text = Convert.ToString(resultNodesResolvedNum);
  cpuLoopsNum.Text = Convert.ToString(resultCpuLoopsNum);
  shortestPathCostNum.Text = Convert.ToString(resultShortestPathCost);
}
Gene
  • 4,192
  • 5
  • 32
  • 56
  • Thanks for your help, it's working now. But I don't quite understand why it needs to be an `Action` object. When doesn't the Invoke require an Action object rather than just a function name? – Luke Mar 23 '12 at 09:12
  • 1
    That's because this method can be used to call any method, not only a method with a given signature. Have a look at [this overload](http://msdn.microsoft.com/en-us/library/a1hetckb.aspx). – Gene Mar 23 '12 at 09:22
  • Additionally, you'll find a good explanation [in this question](http://stackoverflow.com/questions/253138/anonymous-method-in-invoke-call). – Gene Mar 23 '12 at 09:29
1

You can use:

myform.Invoke(ShowResults);
Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
1

There's other options here too:

Alternately use a System.Forms.Timer to call ShowResults periodically. Or another option would be not to use another thread to do the operation; do it in the GUI thread and call Application.DoEvents() from within the operation when you want to let the GUI update.

The first option is nice because it keeps you from accidentally flooding the GUI with Invoke requests, and the second option is nice because it's all on the GUI thread and allows you to have fine-grain control over when things get displayed on the GUI.

Dax Fohl
  • 10,654
  • 6
  • 46
  • 90