C# 曲线编写总览
目录
- 前言
- 一、LiveChart库
- 1.代码编写
- 2.其他 - 实时数据
- 二、OxyPlot库
- 1.代码编写
- 2.其他 - 实时数据
- 三、ScottPlot库
- 1.代码编写
- 2.其他 - 实时数据
- 总结
前言
在项目中曲线是一个常用功能,这篇是整理所用曲线库:livechart库、oxyplot库、scottplot库,如何生成曲线。
一、LiveChart库
1.代码编写
1、xaml方面,代码如下:
//引用livechart库
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"<lvc:CartesianChart Grid.Row="1" DisableAnimations="True"Series="{Binding SeriesCollection}" LegendLocation="Top"><lvc:CartesianChart.DataTooltip><lvc:DefaultTooltip Background="White" FontSize="11" SelectionMode="SharedYValues" /></lvc:CartesianChart.DataTooltip><!--X轴--><lvc:CartesianChart.AxisX><!--MinValue = "0" : 强制从0开始 --><lvc:Axis Title="时间" Foreground="Black" Labels="{Binding Labels}"><lvc:Axis.Separator><lvc:Separator Step="1" StrokeThickness="1" /></lvc:Axis.Separator></lvc:Axis></lvc:CartesianChart.AxisX><!--Y轴--><lvc:CartesianChart.AxisY><lvc:Axis Title="测试值" Foreground="Black" MaxValue="675" MinValue="0"><lvc:Axis.Separator><lvc:Separator Step="100" StrokeThickness="1" /></lvc:Axis.Separator></lvc:Axis></lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
代码分析以及属性扩展:
- 图表基础属性
1、Series:绑定图表的数据系列集合(Y轴数据集)
2、LegendLocation:控制图例位置(如Top、Bottom、Left、Right)
3、DisableAnimations:禁用图表动画(True/Flase),提升性能 - 动画会消耗计算资源,可能导致卡顿。
4、zoom:用于配置图表的缩放和拖拽行为,需配合ZoomingMode枚举使用,适用图表数据量较大或局部放大查看细节。
5、DataTooltip:用于控制鼠标悬停时显示的数据提示框(Tooltip),可以自定义样式、内容、触发方式等。 - 坐标轴属性(AxisX / AxisY)
1、Title:坐标轴标题(如“测试值”)。
2、Labels:绑定X坐标轴标签集合(如{Binding Labels})
3、MinValue / MaxValue:强制设置坐标轴范围(如 MinValue = “0”),一般常用于X、Y轴 0 点。
4、Foreground:坐标轴文字颜色
5、Separator:控制刻度分隔线:Step:刻度间隔(如Step = “100”), StroleThickness:分隔线粗细
2、ViewModel方面,代码如下:
class LiveChartViewModel : ObservableObject
{//属性public SeriesCollection SeriesCollection { get; set; }public List<string> Labels { get; set; }//构造函数public LiveChartViewModel(){// x轴数据集 5个空字符串,对应5个数据点Labels = new List<string> {"", "", "", "", ""}; SeriesCollection = new SeriesCollection{//初始化系列集合new LineSeries{Title = "实时数据", //系列的名称(会显示在图例中)//需ADD{x,y}//Values = new ChartValues<ObservablePoint>(), //数据点的集合Values = new ChartValues<double>{ 0, 0, 0, 0, 0 },PointGeometrySize = 1 //数据点的大小(像素)}}}
}
功能:数据添加
- 完整数据添加,代码如下:
//初始化X轴标签
Labels = new List<string> {"Jan", "Feb", "Mar", "Apr", "May"};//初始化系统数据
SeriesCollection = new SeriesCollection
{new LineSeries //折线图系列{Title = "Sales",Values = new ChartValues<double> {50, 120, 300, 250, 400},Stroke = Brushes.Blue,Fill = Brushes.Transparent}
}
- 动态单个数据添加,代码如下:
Labels.Add(数据);
SeriesCollection[0].Values.Add(数据);
2.其他 - 实时数据
实时数据显示,代码如下:
//实时数据按钮点击事件
public void RealDatesTestCommandExecute()
{Task.Run(() => lineStart());
}/// <summary>
/// 实时数据运行
/// </summary>
public void lineStart()
{Random r = new Random();while (true){Thread.Sleep(1000);double _trend = r.NextDouble() * 500;//通过Dispatcher在工作线程中更新窗体的UI元素Application.Current.Dispatcher.Invoke(() =>{//更新横坐标时间Labels.Add(DateTime.Now.ToString("HH:mm:ss"));Labels.RemoveAt(0);//更新纵坐标数据SeriesCollection[0].Values.Add(_trend);SeriesCollection[0].Values.RemoveAt(0);});}
}
二、OxyPlot库
1.代码编写
1、xaml方面,代码如下:
//引用oxyplot库
xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"<oxy:PlotView Grid.Row="1" Model="{Binding PlotModel}" />
2、ViewModel方面,代码如下:
class OxyPlotViewModel : ObservableObject
{private PlotModel plotModel;public PlotModel PlotModel{get { return plotModel; }set { SetProperty(ref plotModel, value); }}//构造函数public OxyPlotViewModel(){CreateCurve();}public void CreateCurve(){PlotModel = new PlotModel();//单曲线var lineSeries = new LineSeries{//数据点不显示形状MarkerType = MarkerType.None // 或者使用 lineSeries.MarkerType = MarkerType.None;};PlotModel.Series.Add(lineSeries);//1:DateTimeAxis(时间轴) LinearAxis(数值轴)var xAxis = new DateTimeAxis{Position = AxisPosition.Bottom,Title = "时间",StringFormat = "HH:mm:ss", //时间显示格式MajorGridlineStyle = LineStyle.Solid,MinorGridlineStyle = LineStyle.Dot};PlotModel.Axes.Add(xAxis);var yAxis = new LinearAxis{Position = AxisPosition.Left,Title = "测试值"};PlotModel.Axes.Add(yAxis);}
}
功能:数据添加,代码如下:
var temp = PlotModel.Series[0] as LineSeries;temp.Points.Add(new DataPoint(数据1, 数据2));
PlotModel.InvalidatePlot(true); //刷新图表
2.其他 - 实时数据
实时数据显示,代码如下:
//实时数据按钮点击事件
public void RealDatesTestCommandExecute()
{Task.Run(() => LoadCurvesDates());
}/// <summary>
/// 实时加载曲线数据
/// </summary>
public void LoadCurvesDates()
{var temp = PlotModel.Series[0] as LineSeries;Random r = new Random();while (true){Thread.Sleep(1000);double element = r.NextDouble() * 500;DateTime labels = DateTime.Now;//将DateTime转换为OLE Automation日期(double)double timestamp = labels.ToOADate(); temp.Points.Add(new DataPoint(timestamp, element));PlotModel.InvalidatePlot(true); //刷新图表}
}
三、ScottPlot库
1.代码编写
1、xaml方面,代码如下:
//引用ScottPlot库
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"<wpf:WpfPlot Grid.Row="1" Name="WpfPlot1" />
2、xaml.cs方面,代码如下:
public partial class ScottPlotPage : UserControl
{public ScottPlotPage(){InitializeComponent();WpfPlot1.Plot.Style(ScottPlot.Style.Light1);//初始化图表属性WpfPlot1.Plot.Title("测试曲线");WpfPlot1.Plot.XLabel("时间");WpfPlot1.Plot.YLabel("测试值");WpfPlot1.Plot.Legend(); //显示图例//配置X轴为时间格式WpfPlot1.Plot.XAxis.DateTimeFormat(true); //启用时间格式WpfPlot1.Plot.XAxis.TickLabelFormat("HH:mm:ss", dateTimeFormat: true); //设置显示格式WpfPlot1.Plot.SetAxisLimits(xMin: 0, xMax: 10, yMin: 0, yMax: 600); //初始范围 x:0-10 y: 0-600 }
}
功能:数据添加
基础变量定义,代码如下:
var plt = new ScottPlot.Plot(600, 400);//添加XY坐标数据
double[] xs = {1, 2, 3, 4, 5};
double[] ys = {1, 4, 9, 16, 25};
绘制数据方法
1)AddScatter
- 功能:绘制离散散点,默认不连接数据点(仅显示标记点)。
- 特点:
- 适合展示原始数据点,不自动连线。
- 可通过参数lineStyle设置为非None来连接点。
var scatter = plt.AddScatter(xs, ys);
scatter.LineStyle = LineStyle.Solid; //手动启用连线
2)AddScatterLines
- 功能:绘制散点+连线,默认连接所有数据点(带标记的折线图)。
- 特点:
- 是AddScatter的变体,默认启用连线(LineStyle.Solid)。
- 适合需要同时显示数据点和趋势的场景。
var scatterLines = plt.AddScatterLines(xs, ys, label: "趋势线");
3)AddLine
- 功能:绘制单一线段(两点之间的直线)。
- 特点:
- 仅需定义起点和终点(x1, y1, x2, y2), 不处理多数据点。
- 适合绘制辅助线、参考线或简单线段。
plt.AddLine(0, 0, 10, 5, color: Color.Red, lineWith: 2);
4)AddSignal
- 功能:绘制均匀采样信号(高性能折线图)。
- 特点:
- 专为大数据量优化(如音频、传感器数据)。
- 要求X轴为等间隔递增(自动生成0, 1, 2 … 或通过sampleRate指定间隔)。
- 渲染速度极快(使用GPU优化),但不支持任意X坐标。
double[] ys = new double[100_000]; // 10万点数据
var signal = plt.AddSignal(ys, sampleRate: 20_000); // 采样率20kHz//添加仅Y值数据(自动生成0,1,2 ...)
double[] values = {5, 3, 7, 4, 6};
plt.AddSignal(values);
方法总结:
方法 | 数据点连接 | 标记显示 | 适用数据量 | 用途 |
---|---|---|---|---|
AddScatter | 可选 | 是 | 中小 | 仅需标记点展示 |
AddScatterLines | 是 | 是 | 中小 | 需要标记点 + 连线(带标记的趋势线) |
AddLine | 两点一线 | 否 | - | 绘制简单线段(参考线、辅助线) |
AddSignal | 是 | 否 | 超大 | 等间隔信号(如音频、传感器) |
2.其他 - 实时数据
实时数据显示,代码如下:
//属性
private double[] xdates = new double[10000]; //x轴 - 数据(时间轴)
private double[] ydates = new double[10000]; //y轴 - 测试值
private int nextIndex = 0; //下一个写入位置
private bool bufferFull = false; //缓冲区是否已填满private DispatcherTimer timer;/// <summary>
/// 按钮点击事件 - 实时数据测试
/// </summary>
public void RealDatesTestCommand(object sender, RoutedEventArgs e)
{// 重置数据缓冲区Array.Clear(xdates, 0, xdates.Length);Array.Clear(ydates, 0, ydates.Length);nextIndex = 0;bufferFull = false;// 重置图表WpfPlot1.Plot.Clear();WpfPlot1.Plot.SetAxisLimits(0, 10, 0, 600);WpfPlot1.Render();// 初始化定时器(如果未初始化)if (timer == null){timer = new DispatcherTimer{Interval = TimeSpan.FromMilliseconds(1000) // 1秒定时器};timer.Tick += (s, e) =>{// 模拟数据double newValue = new Random().NextDouble() * 600;AddDataPoint(newValue);};}// 启动定时器timer.Start();
}// 添加新数据点
public void AddDataPoint(double yValue)
{// 1. 计算当前时间(OADate格式)double currentTime = DateTime.Now.ToOADate();// 2. 写入缓冲区xdates[nextIndex] = currentTime;ydates[nextIndex] = yValue;// 3. 更新索引nextIndex++;if (nextIndex >= xdates.Length){nextIndex = 0;bufferFull = true;}// 4. 更新显示UpdatePlot();
}//更新图表显示
private void UpdatePlot()
{// 获取当前有效的连续数据段double[] xValid, yValid;if (bufferFull){// 缓冲区已满,数据是环形存储的xValid = new double[xdates.Length];yValid = new double[ydates.Length];// 重组数据(旧数据在前,新数据在后)Array.Copy(xdates, nextIndex, xValid, 0, xdates.Length - nextIndex);Array.Copy(ydates, nextIndex, yValid, 0, ydates.Length - nextIndex);Array.Copy(xdates, 0, xValid, xdates.Length - nextIndex, nextIndex);Array.Copy(ydates, 0, yValid, xdates.Length - nextIndex, nextIndex);}else{// 缓冲区未满,直接取前nextIndex个数据xValid = xdates.Take(nextIndex).ToArray();yValid = ydates.Take(nextIndex).ToArray();}// 更新曲线数据var plot = WpfPlot1.Plot.GetPlottables().FirstOrDefault() as ScottPlot.Plottable.ScatterPlot;if (plot != null){plot.Update(xValid, yValid);}else{WpfPlot1.Plot.AddScatterLines(xValid, yValid);}// 更新时间轴范围UpdateTimeAxis();// 刷新图表WpfPlot1.Render();
}//自动调整时间轴范围
private void UpdateTimeAxis()
{if (nextIndex == 0 && !bufferFull) return;double newestTime = bufferFull ?xdates[nextIndex == 0 ? xdates.Length - 1 : nextIndex - 1] :xdates[nextIndex - 1];// 显示最近60秒数据WpfPlot1.Plot.SetAxisLimitsX(newestTime - 60.0 / 86400, newestTime);
}
总结
库类型 | 优点 | 缺点 |
---|---|---|
LiveChart库 | 曲线美观,wpf中曲线数据能使用绑定Binding | 实时数据量庞大,会卡 |
OxyPlot库 | wpf中曲线数据能使用绑定Binding | 比livechart库实时数据量大点,比scottplot库实时数据量小 |
ScottPlot库 | 数据量最大 | wpf中曲线数据不能使用绑定Binding |
如果想要界面好看,可使用LiveChart库。如果想要界面好看一点并追求点实时效率,可使用OxyPlot库。如果追求极致实时数据效率,可使用ScottPlot库。