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

Live Charts技术问题:如何向第二个Y轴动态添加数据

Live Charts笛卡尔折线图多系列多Y轴动态数据问题修复

核心问题分析

  • 绑定不匹配:XAML中CartesianChart.Series绑定的是ChartValues和ChartValues2,但后台代码创建的是ChartSeries和ChartSeries2,两者完全不对应;且Live Charts仅支持绑定单个SeriesCollection,创建两个集合是错误用法。
  • 未指定Y轴关联:所有系列默认使用第一个Y轴,数据范围差异大(比如电流0-800mA、电压0-8000mV)会导致部分系列数据被压缩到几乎不可见。
  • 数据更新逻辑缺失:未确保UI线程更新,且未正确维护X轴标签与数据点的对应关系。

修正后的代码实现

XAML部分

<lvc:CartesianChart  x:Name="lc_MainChart" 
                     Hoverable="False" 
                     Grid.Row="0" 
                     Grid.Column="1" 
                     DisableAnimations="True" 
                     LegendLocation="Right" 
                     Loaded="CartesianChart_Loaded" 
                     Margin="2,2,2,2" 
                     BorderBrush="Black" 
                     BorderThickness="2,2,2,2"
                     Series="{Binding ChartSeries}"> <!-- 绑定到单个SeriesCollection -->
    <lvc:CartesianChart.AxisX >
        <lvc:Axis Title="DATA POINTS" 
                  FontSize="18" 
                  MinValue="0" 
                  MaxValue="512" 
                  Labels="{Binding Labels}"/>
    </lvc:CartesianChart.AxisX>

    <lvc:CartesianChart.AxisY>
        <!-- 第一个Y轴(索引0):对应电压、速度、力 -->
        <lvc:Axis Title="MOTOR VOLTAGE/SPEED/FORCE" 
                  FontSize="18" 
                  MinValue="0" 
                  MaxValue="8000" />
        <!-- 第二个Y轴(索引1):对应电流 -->
        <lvc:Axis Title="MOTOR CURRENT (mA)" 
                  FontSize="18" 
                  MinValue="0" 
                  MaxValue="800" 
                  Position="RightTop" />
    </lvc:CartesianChart.AxisY>
</lvc:CartesianChart>

C#后台代码(需实现INotifyPropertyChanged)

using LiveCharts;
using LiveCharts.Wpf;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

public class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    // 单个SeriesCollection包含所有系列
    private SeriesCollection _chartSeries;
    public SeriesCollection ChartSeries
    {
        get => _chartSeries;
        set
        {
            _chartSeries = value;
            OnPropertyChanged(nameof(ChartSeries));
        }
    }

    // X轴标签集合
    private ObservableCollection<string> _labels;
    public ObservableCollection<string> Labels
    {
        get => _labels;
        set
        {
            _labels = value;
            OnPropertyChanged(nameof(Labels));
        }
    }

    public MainWindow()
    {
        InitializeComponent();
        InitializeChart();
        DataContext = this;
    }

    private void InitializeChart()
    {
        // 初始化系列集合,每个系列指定对应的Y轴索引
        ChartSeries = new SeriesCollection
        {
            // 电机电流:绑定到第二个Y轴(索引1)
            new LineSeries
            {
                Title = "Motor Current",
                Values = new ChartValues<short> { 0 },
                PointForeground = Brushes.Red,
                PointGeometry = null,
                YAxisIndex = 1 // 指定使用右侧Y轴
            },
            // 电机电压:绑定到第一个Y轴(索引0)
            new LineSeries
            {
                Title = "Voltage",
                Values = new ChartValues<short> { 0 },
                PointForeground = Brushes.Blue,
                PointGeometry = null,
                YAxisIndex = 0
            },
            // 电机速度:绑定到第一个Y轴(索引0)
            new LineSeries
            {
                Title = "Motor Speed",
                Values = new ChartValues<short> { 0 },
                PointForeground = Brushes.Green,
                PointGeometry = null,
                YAxisIndex = 0
            },
            // 力:绑定到第一个Y轴(索引0)
            new LineSeries
            {
                Title = "Force",
                Values = new ChartValues<short> { 0 },
                PointForeground = Brushes.Orange,
                PointGeometry = null,
                YAxisIndex = 0
            },
            // 第五类数据:根据需求添加,指定对应Y轴
            new LineSeries
            {
                Title = "Other Data",
                Values = new ChartValues<short> { 0 },
                PointForeground = Brushes.Purple,
                PointGeometry = null,
                YAxisIndex = 0
            }
        };

        // 初始化X轴标签
        Labels = new ObservableCollection<string> { "0" };
    }

    // 模拟从串口/文件读取数据并更新图表的方法
    private void UpdateChart(short motorCurrent, short vBat, short motorSpeed, short force, short otherData)
    {
        // 确保在UI线程操作
        Dispatcher.Invoke(() =>
        {
            // 添加新数据点到对应系列
            ChartSeries[0].Values.Add(motorCurrent);
            ChartSeries[1].Values.Add(vBat);
            ChartSeries[2].Values.Add(motorSpeed);
            ChartSeries[3].Values.Add(force);
            ChartSeries[4].Values.Add(otherData);

            // 更新X轴标签(保持与数据点数量一致)
            Labels.Add(Labels.Count.ToString());

            // 限制数据点数量为512(匹配X轴MaxValue)
            if (ChartSeries[0].Values.Count > 512)
            {
                foreach (var series in ChartSeries)
                {
                    (series.Values as ChartValues<short>).RemoveAt(0);
                }
                Labels.RemoveAt(0);
            }
        });
    }

    private void CartesianChart_Loaded(object sender, RoutedEventArgs e)
    {
        // 可在此处启动数据读取线程
        // 例如:Task.Run(() => ReadDataFromSerialPort());
    }
}

关键修复点说明

  • 统一SeriesCollection:所有数据系列都放入同一个SeriesCollection,通过YAxisIndex指定关联的Y轴(0对应左侧Y轴,1对应右侧Y轴)。
  • 实现INotifyPropertyChanged:确保绑定属性更新时UI自动刷新。
  • UI线程更新:串口/文件读取通常在后台线程,必须通过Dispatcher回到UI线程更新图表数据。
  • 数据点数量控制:保持X轴标签与数据点数量一致,超出512时移除最旧数据,避免图表溢出。

内容的提问来源于stack exchange,提问作者user3119168

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 17:58:10