-1

I am attempting to plot two graphs simultaneously using multi threading, however, the charts return an error message stating "Control 'chart1' accessed from a thread other than the thread it was created on.". I believe this can be resolved using "Invoke", but I am unsure of how this can be done.

Here is simplified code for one graph and one thread:

private void button1_Click(object sender, EventArgs e)
    {
        Thread th = new Thread(thread);
        th.Start();            
    }


    public void graph(List<double> xlist, List<double> ylist)
    {            
        chart1.Series["1"].Points.DataBindXY(xlist, ylist);            
    }


    public void thread()
    {
        List<double> xlist = new List<double>();
        List<double> ylist = new List<double>();            

        //Assume xlist and ylist have a range of numerical elements

        graph(xlist, ylist);
    }

Any help would be appreciated.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Rob
  • 1

2 Answers2

0

This is due to the fact that whilst using Windows Forms, the only thread that can update the GUI is the main thread. You can get around this by using an async await pattern, so that calculations are run in the background and by invokeing the methods required to update your GUI.

Here's an example:

private void MyMethod(var myObject)
{

    if (form.myControl.InvokeRequired)
    {

        form.myControl.Invoke((MethodInvoker)delegate {
          // Running on the UI thread
          form.myControl.MyMethod(myObject);
          });
    // Back on the worker thread
    }
    else //Must already be on the UI thread
    {
         form.myControl.MyMethod(myObject);
    }

}

In this method we check to see whether or not the code is running on the main thread with InvokeRequired, if it isn't, we create a MethodInvoker from a delegate and run our change on the main thread using control.Invoke(). If it is already on the main thread, we just make our change.

Also, see these resources:

https://www.dotnetperls.com/async

https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls

ScottishTapWater
  • 3,656
  • 4
  • 38
  • 81
0

This is how it's done:

chart1.Invoke(c => c.Series["1"].Points.DataBindXY(xlist, ylist), new object[] { chart1 });

For easier use in multiple places you could write an extension method:

public static class ControlExtensions
{
    public static void UpdateOnUIThread<T>(this T control, Action<T> action) where T : ISynchronizeInvoke
    {
        if (control.InvokeRequired)
            control.Invoke(action, new object[] { control });
        else
            action(control);
    }
}

and use it as:

chart1.UpdateOnUIThread(c => c.Series["1"].Points.DataBindXY(xlist, ylist));
Robert Synoradzki
  • 1,766
  • 14
  • 20