C# 曲线编写总览

目录


前言

在项目中曲线是一个常用功能,这篇是整理所用曲线库:livechart库、oxyplot库、scottplot库,如何生成曲线。


一、LiveChart库

1.代码编写

1、xaml方面,代码如下:

csharp 复制代码
//引用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方面,代码如下:

csharp 复制代码
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   //数据点的大小(像素)
    		}
		}
	}
}

功能:数据添加

  • 完整数据添加,代码如下:
csharp 复制代码
//初始化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
	}
}
  • 动态单个数据添加,代码如下:
csharp 复制代码
Labels.Add(数据);
SeriesCollection[0].Values.Add(数据);

2.其他 - 实时数据

实时数据显示,代码如下:

csharp 复制代码
//实时数据按钮点击事件
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方面,代码如下:

csharp 复制代码
//引用oxyplot库
xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"

<oxy:PlotView Grid.Row="1" Model="{Binding PlotModel}" />

2、ViewModel方面,代码如下:

csharp 复制代码
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);
	}
} 

功能:数据添加,代码如下:

csharp 复制代码
var temp = PlotModel.Series[0] as LineSeries;

temp.Points.Add(new DataPoint(数据1, 数据2));
PlotModel.InvalidatePlot(true); //刷新图表

2.其他 - 实时数据

实时数据显示,代码如下:

csharp 复制代码
//实时数据按钮点击事件
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方面,代码如下:

csharp 复制代码
//引用ScottPlot库
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

<wpf:WpfPlot Grid.Row="1" Name="WpfPlot1" />

2、xaml.cs方面,代码如下:

csharp 复制代码
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 
	}
}

功能:数据添加

基础变量定义,代码如下:

csharp 复制代码
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来连接点。
csharp 复制代码
var scatter = plt.AddScatter(xs, ys);
scatter.LineStyle = LineStyle.Solid; //手动启用连线

2)AddScatterLines

  • 功能:绘制散点+连线,默认连接所有数据点(带标记的折线图)。
  • 特点
    • 是AddScatter的变体,默认启用连线(LineStyle.Solid)。
    • 适合需要同时显示数据点和趋势的场景。
csharp 复制代码
var scatterLines = plt.AddScatterLines(xs, ys, label: "趋势线");

3)AddLine

  • 功能:绘制单一线段(两点之间的直线)。
  • 特点
    • 仅需定义起点和终点(x1, y1, x2, y2), 不处理多数据点。
    • 适合绘制辅助线、参考线或简单线段。
csharp 复制代码
plt.AddLine(0, 0, 10, 5, color: Color.Red, lineWith: 2);

4)AddSignal

  • 功能:绘制均匀采样信号(高性能折线图)。
  • 特点
    • 专为大数据量优化(如音频、传感器数据)。
    • 要求X轴为等间隔递增(自动生成0, 1, 2 ... 或通过sampleRate指定间隔)。
    • 渲染速度极快(使用GPU优化),但不支持任意X坐标。
csharp 复制代码
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.其他 - 实时数据

实时数据显示,代码如下:

csharp 复制代码
//属性
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库。

相关推荐
上元星如雨2 小时前
在WPF中添加动画背景
wpf
Kookoos2 小时前
ABP VNext + Orleans:Actor 模型下的分布式状态管理最佳实践
分布式·后端·c#·.net·.netcore·abp vnext
csdn_aspnet2 小时前
C# 高效读取大文件
c#
若汝棋茗5 小时前
C# 异步方法中缺少 `await` 运算符的隐患与解决方案
开发语言·c#·await
高远-临客6 小时前
unity控制相机围绕物体旋转移动
unity·c#·游戏引擎
宝桥南山6 小时前
.NET 10 - 尝试一下Minimal Api的Validation新特性
microsoft·微软·c#·asp.net·.net·.netcore
上元星如雨8 小时前
在WPF程序中设置背景图片
大数据·hadoop·wpf
时光追逐者9 小时前
一个开源的 Blazor 跨平台入门级实战项目
c#·asp.net·.net core·blazor