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

how to temporarily pause drawing updates of a realtime data chart

我想要“暂停”图表的系列更新来做一些工作(比如我有一个按钮,当我点击它时它会暂停图表更新,然后当我点击恢复按钮时,它会更新所有暂停点系列。

我知道

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();

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

MSDN 是这样说的:

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.

这可以解释为什么它对您不起作用:保持通话 平衡 至关重要。您需要自己跟踪它们,因为没有您可以查询的计数器。但是如果你超过了 ResumeUpdates 调用,没有什么不好的事情发生,额外的调用被简单地忽略并且下一个 SuspendUpdates 将再次暂停。

这是一个示例屏幕截图,请注意暂停计数器..!

请注意,通常添加点数会自动触发Invalidate。如果你正在做其他事情,比如在 Paint 事件中绘图等。你可能需要调用 Chart.Invalidate()SuspendUpdates 会阻止,直到被相同数量的 [=11] 取消=]..


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

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

当设置为暂停模式时,只需将所有点添加到 series.Points,然后再清除它。不幸的是没有 points.AddRange 所以你将不得不使用 foreach 循环。也许 chart.SuspendLayout 可以帮助提高性能。

  • 想到的其他解决方法可能适合也可能不适合:您可以使用 xAxis.MaximumxAxis.Minimum values。通过将它们设置为固定值,您将允许在不显示它们的情况下向右侧添加点。要显示整组点,您可以将它们重置为 double.NaN。这可能对您有用,但也可能会干扰您拥有的东西。

更新: 如 OP 所述,当他更改 [=30 的 Minimum and/or Maximum 时,数据会更新=].同样的效果会出现在很多其他场合:

  • 呼叫chart.AreasRecalculateAxesScale();
  • 更改图表的 Size
  • 改变任意轴属性如ColorWidth..
  • 正在更改 Series
  • LegendText
  • 还有更多..

所以我想每当 ChartArea 被操纵并被迫自我更新时都需要更新的数据..

因此,这很可能会使第一个解决方法变得更好,因为 更强大 解决方案。