Search code examples
c#winformschartsbackgroundworkerdata-handling

C# Adding points to Chart in realtime


I have got an question about adding points to charts.

My windows forms application is using a thread to get the Y value from another server. Every 500ms I get a new value(string) which should be added as a point, but I have no idea how to do that. It would be really nice if the points will be displayed in realtime and not only after ending the process. I think it is not a real difficult question but I didn`t find a solution.

Thread:

 private void Work()
    {
        int counter = 0;

        while (true)
        {
            counter++;
            WebClient code = new WebClient();
            speed_str = code.DownloadString("http://192.168.19.41/speedfile.html");
            speedval = Convert.ToDouble(speed_str);
            Console.WriteLine(speedval.ToString() + "\n Times executed: "  + counter);
            Thread.Sleep(1000);
        }
    }

Configuration and chart

 Thread thread = new Thread(new ThreadStart(this.Work));
        thread.IsBackground = true;
        thread.Name = "My Worker.";
        thread.Start();

        //Speed
        Series speed = new Series("Speed[m/s]");

        speed.ChartType = SeriesChartType.Spline;


        //Engines Left 
        engleft = new Series("Engines Left");

        engleft.ChartType = SeriesChartType.Spline;

        Engines.Series.Add(engleft);

        engleft.Points.Clear();

        string speed_read = Console.ReadLine();

Thanks for help :)


Solution

  • You have a classic Producer-Consumer scenario on your hands.

    Which means that one thread produces an item (the background thread)
    While another consumes it (the UI thread).

    One way to communicate between a producer and a consumer is with an event :

        class Producer
        {
    
        public event EventHandler<double> YSeriesEvent;
        private Thread thread;
    
        public Producer()
        {
            thread = new Thread(new ThreadStart(this.Work));
            thread.IsBackground = true;
            thread.Name = "My Worker.";     
        }
    
        public void Start()
        {
            thread.Start();
        }
    
        private void Work()
        {
            int counter = 0;
    
            while (true)
            {
                counter++;
                WebClient code = new WebClient();
                speed_str = code.DownloadString("http://192.168.19.41/speedfile.html");
                speedval = Convert.ToDouble(speed_str);
                YSeriesEvent?.Invoke(this, speedval);
            }
        }
       }
    

    Then, in your form you may do like so:

    class MyForm : Form
    {
    
    private Producer producer;
    
    public MyForm()
    {
        producer = new Producer();
        producer.YSeriesEvent += MyHandler ;
        Load+= (sender, args) => producer.Start();
    }
    
    private void MyHandler(object o, double val)
    {
        Invoke(new Action(() =>
        {
               //add value to chart here
        }));
    }
    }   
    

    Note that WinForms is single threaded, meaning that no thread can do work directly on a UI element, unless it is the UI thread.
    This is why I called the Invoke method - it simply offloads work to be carried out on the UI thread.