3

I would like a “Pause” the chart's series updates to do some job (like i have a button when i click it will suspend the chart update and then when I click resume button, it will update all suspended point in series.

I know about

chart1.Series.SuspendUpdates();

but it does not seem to work with me. I use mschart sample -- realtime data (thread safe).

Here is the full code

public partial class RealTimeSample : Form
{
    public RealTimeSample()
    {
        InitializeComponent();
    }
    private Thread addDataRunner;
    private Random rand = new Random();

    public delegate void AddDataDelegate();
    public AddDataDelegate addDataDel;
    private void RealTimeSample_Load(object sender, System.EventArgs e)
    {

        // create the Adding Data Thread but do not start until start button clicked
        ThreadStart addDataThreadStart = new ThreadStart(AddDataThreadLoop);
        addDataRunner = new Thread(addDataThreadStart);

        // create a delegate for adding data
        addDataDel += new AddDataDelegate(AddData);

    }



    /// Main loop for the thread that adds data to the chart.
    /// The main purpose of this function is to Invoke AddData
    /// function every 1000ms (1 second).
    private void AddDataThreadLoop()
    {
        while (true)
        {
            chart1.Invoke(addDataDel);

            Thread.Sleep(1000);
        }
    }

    public void AddData()
    {
        DateTime timeStamp = DateTime.Now;

        foreach (Series ptSeries in chart1.Series)
        {
            AddNewPoint(timeStamp, ptSeries);
        }
    }

    /// The AddNewPoint function is called for each series in the chart when
    /// new points need to be added.  The new point will be placed at specified
    /// X axis (Date/Time) position with a Y value in a range +/- 1 from the previous
    /// data point's Y value, and not smaller than zero.
    public void AddNewPoint(DateTime timeStamp, System.Windows.Forms.DataVisualization.Charting.Series ptSeries)
    {
        double newVal = 0;

        if (ptSeries.Points.Count > 0)
        {
            newVal = ptSeries.Points[ptSeries.Points.Count - 1].YValues[0] + ((rand.NextDouble() * 2) - 1);
        }

        if (newVal < 0)
            newVal = 0;

        // Add new data point to its series.
        chart1.Series.SuspendUpdates();
        ptSeries.Points.AddXY(timeStamp.ToOADate(), rand.Next(10, 20));
        chart1.Series.SuspendUpdates();
        // remove all points from the source series older than 1.5 minutes.
        double removeBefore = timeStamp.AddSeconds((double)(90) * (-1)).ToOADate();
        //remove oldest values to maintain a constant number of data points
        while (ptSeries.Points[0].XValue < removeBefore)
        {
            ptSeries.Points.RemoveAt(0);
        }

        chart1.ChartAreas[0].AxisX.Minimum = ptSeries.Points[0].XValue;
        chart1.ChartAreas[0].AxisX.Maximum = DateTime.FromOADate(ptSeries.Points[0].XValue).AddMinutes(2).ToOADate();


    }

    /// Clean up any resources being used.
    protected override void Dispose(bool disposing)
    {
        if ((addDataRunner.ThreadState & ThreadState.Suspended) == ThreadState.Suspended)
        {
            addDataRunner.Resume();
        }
        addDataRunner.Abort();

        if (disposing)
        {
            if (components != null)
            {
                components.Dispose();
            }
        }
        base.Dispose(disposing);
    }

    private void startTrending_Click_1(object sender, EventArgs e)
    {
        // Disable all controls on the form
        startTrending.Enabled = false;
        // and only Enable the Stop button
        stopTrending.Enabled = true;

        // Predefine the viewing area of the chart
        var minValue = DateTime.Now;
        var maxValue = minValue.AddSeconds(120);

        chart1.ChartAreas[0].AxisX.Minimum = minValue.ToOADate();
        chart1.ChartAreas[0].AxisX.Maximum = maxValue.ToOADate();

        // Reset number of series in the chart.
        chart1.Series.Clear();

        // create a line chart series
        Series newSeries = new Series("Series1");
        newSeries.ChartType = SeriesChartType.Line;
        newSeries.BorderWidth = 2;
        newSeries.Color = Color.OrangeRed;
        newSeries.XValueType = ChartValueType.DateTime;
        chart1.Series.Add(newSeries);

        // start worker threads.
        if (addDataRunner.IsAlive == true)
        {
            addDataRunner.Resume();
        }
        else
        {
            addDataRunner.Start();
        }
    }


    private void stopTrending_Click_1(object sender, EventArgs e)
    {
        if (addDataRunner.IsAlive == true)
        {
            addDataRunner.Suspend();
        }

        // Enable all controls on the form
        startTrending.Enabled = true;
        // and only Disable the Stop button
        stopTrending.Enabled = false;
    }        
}

EDIT:

I figured out that as long as you set the minmum or the maximum property for the Axis the chart will keep display even if you have used

chart1.Series.SuspendUpdates();

I had to to remove those lines after i call SuspendUpdates() and now i can see the chart series suspended

chart1.ChartAreas[0].AxisX.Minimum = ptSeries.Points[0].XValue;
chart1.ChartAreas[0].AxisX.Maximum = DateTime.FromOADate(ptSeries.Points[0].XValue).AddMinutes(2).ToOADate();
user1477332
  • 325
  • 2
  • 4
  • 20
  • While in pause mode simply add the DataPoints to a List instead of the series.Points. When resuming add the list and clear it.. – TaW Sep 28 '18 at 20:40
  • thanks for your comment , i used your hint and worked for me, if you put this hint as an answer i will accept it, but do you have any idea why suspendupdates not working ?? – user1477332 Sep 28 '18 at 21:22

1 Answers1

4

MsChart does support this directly and indeed using Series.SuspendUpdates() is a good way but you need to do it right. (See however the update below for a drawback)

MSDN says this:

A call to the Invalidate method will have no effect after the SuspendUpdates method is called.

If you call the SuspendUpdates method several times, you will need to call the ResumeUpdates method an equal number of times.

This would explain why it doesn't work for you: Keeping the calls balanced is crucial. You need to keep track of them yourself as there is no counter you could query. But if you overshoot the ResumeUpdates calls, nothing bad happens, extra calls are simply ignored and the next SuspendUpdates will pause again.

Here is an example screenshot, watch the suspension counter..!

enter image description here

Note that normally adding points will automatically triggger an Invalidate. If you are doing other things, like drawing in a Paint event etc.. you may need to call Chart.Invalidate(), which SuspendUpdates will prevent, until cancelled by the same number of ResumeUpdates..


Alternatively you can also use one of these simple workarounds:

  • The most straightforward will create the DataPoints via a constructor and then either
    • use series.Add(theNewPoint) for normal, or..
    • use someList<DataPoint>.Add(theNewPoint) for paused mode.

When setting to pause mode simply add all points to the series.Points before clearing it. Unfortunately there is no points.AddRange so you will have to use a foreach loop. Maybe chart.SuspendLayout could help with performance.

  • The other workaround that comes to mind may or may not be suitable: You could play with the xAxis.Maximum and maybe xAxis.Minimum values. By setting them to fixed values you would allow addding points to the right without displaying them. To show the whole set of points you would reset them to double.NaN. This may work for you but it may also interfer with what you have.

Update: As noted by OP, the data are updated when he changes the Minimum and/or Maximum of an Axis. The same effect will show on many other occasions:

  • Calling chart.AreasRecalculateAxesScale();
  • Changing the chart's Size
  • Changing any Axis property like Color or Width..
  • Changing the LegendText of a Series
  • and many more..

So I guess the updated data are needed whenever the ChartArea is manipulated and forced to update itself..

So, this may well make the 1st workaround the better because the more robust solution.

TaW
  • 53,122
  • 8
  • 69
  • 111
  • It does not suspend my drawing,... my problem was that chart do not suspend updates to series ... even when i call it to suspend, the alternative work around (DataPoint list) works with me but i think chart.series.suspend will be better performance, so if you could please put the source could for the image you made , will be very helpful. – user1477332 Sep 29 '18 at 12:39
  • It does not suspend my drawing,... my problem was that chart do not suspend updates to series ... even when i call it to suspend, the alternative work around (DataPoint list) works with me but i think chart.series.suspend will be better performance, so if you could please put the source could for the image you made , will he very helpful. – user1477332 Sep 29 '18 at 12:40
  • If you try the code in my original question ... chart is keep going addpoints even when i call chart1.Series.SuspendUpdates(); twice ...without calling resumeupdates – user1477332 Sep 29 '18 at 12:42
  • I can but there is really __nothing__ to it: A Timer adds points a button calls suspend and increments the numericUpdown, the other button calls Resume and decrements. That is all there is to it! 5 lines of code.. – TaW Sep 29 '18 at 12:43
  • Well of course it keeps adding points as long as the code runs, or in my case the timer. They just don't show. You can observe the jumps when the update is resumed.. Do you mean the display gets updated all the time? Btw: I'm not sure about the perfromance; adding a point to a List is as cheap as it gets. And adding each to the chart is what happens anyway.. – TaW Sep 29 '18 at 12:45
  • Looking at the code I wonder why you are suspending to increbily often? And where that is connected to a pause button or function?? – TaW Sep 29 '18 at 12:49
  • Yes i mean the display keeps updated all the time ... points drawn .. no difference if i call suspend ..... – user1477332 Sep 29 '18 at 13:14
  • For my code now there is no button to suspend ... but i believe that the output for the code .... should display nothing however .. i see points displayed normally – user1477332 Sep 29 '18 at 13:16
  • Well that is strange but I can't reproduce for lack of time. Maybe you want to play with a simpler setup, maybe with a timer to convince yourself that and how it works. Also: If not for a user interaction, why do you want to pause in the first place??? – TaW Sep 29 '18 at 13:21
  • 1
    It was for user , i edited the code before i post here , this code was my last hope to force it to not display anything to see if the suspend works in the first place or not ,... anyway thanks for all the efforts, i will try with simpler example again – user1477332 Sep 29 '18 at 13:32
  • 1
    i figured out why it was not working, check my edit if you have time ,... thanks again for help – user1477332 Sep 29 '18 at 16:20
  • 1
    Very interesting! Thanks for keeping me updated! - I have updated the answer to reflect this and some more and similar observations – TaW Sep 29 '18 at 16:51