如何临时暂停实时数据图表的绘制更新

3
我希望可以在图表的系列更新中添加“暂停”功能,以便进行一些任务(例如,我有一个按钮,当我点击它时,将暂停图表更新,然后当我点击恢复按钮时,它将更新所有已暂停的系列点)。
我知道关于

chart1.Series.SuspendUpdates();

但是看起来对我不起作用。我使用mschart示例——实时数据(线程安全)。

下面是完整的代码:

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;
    }        
}

编辑:

我发现只要设置轴的最小值或最大值属性,即使你使用了

chart1.Series.SuspendUpdates();

在调用SuspendUpdates()之后,我不得不删除那些代码行,现在我可以看到图表系列被暂停了。

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

在暂停模式下,只需将数据点添加到List<DataPoint>而不是series.Points中。恢复时,添加该列表并清除它。 - TaW
谢谢您的评论,我使用了您的提示并且它对我起作用了。如果您将此提示作为答案,我将会接受它。但是,您是否知道为什么suspendupdates没有起作用呢? - user1477332
1个回答

4

MsChart 直接支持此功能,确实使用 Series.SuspendUpdates() 是一个好方法,但您需要正确地操作它。(然而,请参见下面的更新以了解一个缺点)

MSDN 表示:

在调用 SuspendUpdates 方法之后,调用 Invalidate 方法将不起作用。

如果您多次调用 SuspendUpdates 方法,则需要调用 ResumeUpdates 方法相同数量的次数。

这就解释了为什么它对你不起作用:保持调用平衡至关重要。由于没有计数器可供查询,您需要自己跟踪它们。但是,如果您超过ResumeUpdates的调用次数,不会发生任何问题,多余的调用将被忽略,下一个SuspendUpdates将再次暂停。
以下是示例截图,请注意暂停计数器..!

enter image description here

请注意,通常添加点将自动触发 Invalidate。如果您正在执行其他操作,例如在 Paint 事件中绘制等,则可能需要调用 Chart.Invalidate(),而 SuspendUpdates 将阻止此操作,直到通过相同数量的 ResumeUpdates 取消为止。

或者您也可以使用以下简单的解决方法之一:

  • 最直接的方法是通过构造函数创建DataPoints,然后
    • 对于正常模式,使用series.Add(theNewPoint),或者...
    • 对于暂停模式,使用someList<DataPoint>.Add(theNewPoint)

当设置为暂停模式时,在清除之前将所有点添加到series.Points中。不幸的是,没有points.AddRange,因此您将不得不使用foreach循环。也许chart.SuspendLayout可以提高性能。

  • 另一个可行的解决方法可能是:您可以尝试调整xAxis.MaximumxAxis.Minimum的值。通过将它们设置为固定值,您可以允许在右侧添加点而不显示它们。要显示整个点集,您需要将它们重置为double.NaN。这可能适用于您,但也可能会干扰您现有的内容。

更新:正如OP所指出的,当他更改AxisMinimum和/或Maximum时,数据会更新。这种效果也会在许多其他场合显示:

  • 调用chart.AreasRecalculateAxesScale();
  • 更改图表的Size
  • 更改任何 Axis 属性,如ColorWidth
  • 更改SeriesLegendText
  • 等等...

因此,我猜每当ChartArea被操作并强制更新时,需要更新数据。

所以,这可能会使第一种解决方法成为更好的选择,因为它是更健壮的解决方案。


它不会暂停我的绘图,我的问题是图表不会暂停对系列的更新...即使我调用它进行暂停,替代解决方法(DataPoint列表)适用于我,但我认为chart.series.suspend会有更好的性能,所以如果您可以提供制作图像的源代码,将非常有帮助。 - user1477332
我可以,但实际上这很简单:一个计时器添加分数,一个按钮调用暂停并增加数字上下文,另一个按钮调用恢复并减少。就是这样!只需要5行代码... - TaW
1
这是为了用户而做的,我在发布之前编辑了代码,这段代码是我最后的希望,以便强制它不显示任何内容,以查看挂起是否起作用...无论如何,感谢所有的努力,我将再次尝试使用更简单的示例。 - user1477332
1
我找到了为什么它不起作用的原因,如果你有时间,请检查我的编辑...再次感谢你的帮助。 - user1477332
1
非常有趣!感谢您让我保持更新!- 我已经更新了答案以反映这一点,还有一些类似的观察。 - TaW
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接