首页 > 解决方案 > 如何暂时暂停实时数据图表的绘图更新

问题描述

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

我知道

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

编辑:

我发现只要您为 Axis 设置了 minmum 或 maximum 属性,即使您使用过,图表也会保持显示

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

标签: c#.netmschart

解决方案


MsChart确实直接支持这一点,确实使用Series.SuspendUpdates()是一个好方法,但你需要做对。(但是,请参阅下面的更新以了解缺点)

MSDN是这样说的:

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

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

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

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

在此处输入图像描述

请注意,通常添加点会自动触发Invalidate. 如果您正在做其他事情,例如参加Paint活动等。您可能需要调用Chart.Invalidate(),这SuspendUpdates将阻止,直到被相同数量的ResumeUpdates..取消


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

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

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

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

更新:正如 OP 所指出的,当他更改和Minimum/或Maximum. Axis在许多其他场合也会出现同样的效果:

  • 打电话chart.AreasRecalculateAxesScale();
  • 改变图表的Size
  • 更改任何Axis 属性,例如ColorWidth..
  • 改变LegendText一个Series
  • 还有很多..

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

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


推荐阅读