You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForms Chart实时显示ECG数据时旧点无法删除及主网格缩放问题

WinForm Chart 实时ECG显示问题修复方案

核心问题根因

  • 旧数据点无法正确移除:现有删除逻辑遍历规则错误,且删除过程中数据点集合索引动态偏移,实际未删除最早的冗余数据;同时X轴值存储为字符串格式,无法基于时间维度控制显示范围。
  • 网格不规则缩放:X、Y轴默认开启自动适配范围机制,每次数据更新时轴边界动态调整,引发网格缩放;单Tick内频繁调用Update()方法引发的渲染抖动也会放大该异常。

代码修改方案

1. 初始化逻辑调整

固定轴范围、关闭自动适配,X轴改为时间类型适配:

timer1.Interval = 15;
var chartArea = this.chartEKG.ChartAreas["ChartArea1"];
chartArea.CursorX.LineColor = Color.LawnGreen;
chartArea.CursorY.LineColor = Color.LawnGreen;
// Y轴固定范围,可根据实际ECG数值调整上下限
chartArea.AxisY.MajorGrid.Enabled = true;
chartArea.AxisY.MajorGrid.Interval = 200;
chartArea.AxisY.Minimum = 0; // 存在负数值可自行调整,例:-1000
chartArea.AxisY.Maximum = 4000; // 可根据实际信号最大值调整
chartArea.AxisY.Title = "mV";
// X轴改为时间类型,固定显示时长
chartArea.AxisX.MajorGrid.Enabled = true;
chartArea.AxisX.MajorGrid.IntervalType = DateTimeIntervalType.Seconds;
chartArea.AxisX.MajorGrid.Interval = 1; // 每秒1条网格线
chartArea.AxisX.LabelStyle.Format = "HH:mm:ss tt";
chartArea.AxisX.IntervalType = DateTimeIntervalType.Seconds;
// 初始X轴范围
chartArea.AxisX.Minimum = DateTime.Now.ToOADate();
chartArea.AxisX.Maximum = DateTime.Now.AddSeconds(10).ToOADate();
CultureInfo culture = new CultureInfo("en-US");
ekgData = AnesthVSDatas.CreateEKGWaveformObjects(8237);
// 全局定义常量,可按需调整
const int MaxDisplayPoints = 5000; // 500采样率下对应10秒数据量

2. 播放按钮逻辑调整

避免重复注册Tick事件,修复Series配置:

private void butPlayEKG_Click(object sender, EventArgs e) {
    chartEKG.Series.Clear();
    var series1 = new Series {
        Name = "EKG",
        Color = Color.LawnGreen,
        IsVisibleInLegend = true,
        IsXValueIndexed = false, // 关闭X值索引模式,时间轴才能正常生效
        ChartType = SeriesChartType.Line,
        XValueType = ChartValueType.DateTime // 明确X值为时间类型
    };
    this.chartEKG.Series.Add(series1);
    playButtonClicked = true;
    // 先停止原有定时器、清空旧事件绑定,避免重复触发
    timer1.Stop();
    timer1.Tick -= (s, args) => timer1_Tick(series1);
    timer1.Interval = 5;
    timer1.Tick += (s, args) => timer1_Tick(series1);
    timer1.Start();
}

3. 定时器Tick逻辑调整

修复时间计算、删除逻辑,减少不必要的渲染:

private void timer1_Tick(Series series1) {
    labelClock.Text = DateTime.Now.ToString("HH:mm:ss tt");
    // 单次Tick只处理最新的1条ekgData即可,不需要全量遍历
    if(ekgData.Count == 0) return;
    var latestEkg = ekgData[ekgData.Count - 1];
    string str = latestEkg.EKGWaveform.ToString();
    var count = str.Split('^');
    var EKGData = Array.ConvertAll<string, int>(count, Convert.ToInt32);
    string timeStamp = latestEkg.VSTimeStamp.ToString();
    string format = "yyyyMMddHHmmssfff";
    DateTime time = DateTime.ParseExact(timeStamp, format, null);
    // 修复AddSeconds不生效的问题
    double sampleInterval = 1.0 / 500; // 500采样率对应的间隔秒数
    for (int i = 0; i < Math.Min(499, EKGData.Length); i++) {
        DateTime adjTime = time.AddSeconds(i * sampleInterval);
        series1.Points.AddXY(adjTime, EKGData[i] * .61);
    }
    // 超出最大显示点数时,删除最前面的冗余点
    while(series1.Points.Count > MaxDisplayPoints) {
        series1.Points.RemoveAt(0);
    }
    // 动态调整X轴范围,实现滚动效果
    var chartArea = chartEKG.ChartAreas["ChartArea1"];
    if(series1.Points.Count > 0) {
        var latestTime = DateTime.FromOADate(series1.Points.Last().XValue);
        chartArea.AxisX.Maximum = latestTime.ToOADate();
        chartArea.AxisX.Minimum = latestTime.AddSeconds(-10).ToOADate();
    }
    // 整批数据处理完再更新,不要逐点更新减少渲染消耗
    chartEKG.Invalidate();
}

额外优化说明

  • 如果不需要保留历史数据,可直接通过X轴的最小最大值控制显示范围,不需要删除旧点也能实现滚动效果,性能更好
  • 定时器间隔不要设置过短,5ms的间隔小于系统定时器的最小精度(通常15ms左右),实际不会生效,建议调整为10~15ms即可

内容的提问来源于stack exchange,提问作者William J. Starck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 16:45:02