WPF ScottPlot图表库学习笔记
面向刚接触 WPF、已经能写基本 XAML 和事件处理,准备学习“怎样把业务数据画成图表”的读者。
推荐环境:.NET 10 LTS + WPF。随附示例为了适配本机缓存依赖,固定使用.NET Framework 4.6.2与x64,ScottPlot 代码本身同样适用于新版本 WPF。
最后更新:2026-10-23
目录
写在前面
这份笔记解决什么问题
第一次用 WPF 做图表,常见的困难不是“不会写窗口”,而是下面这些:
1 | 图表控件放进去了,但一片空白 |
这一篇只围绕四个步骤展开:
1 | 先引入控件:分清 WpfPlot 与 Plot |
图 1:本篇的知识地图。每个步骤都能单独运行,也能组合成一张工业趋势图。
版本说明
ScottPlot 5 与 ScottPlot 4 的 API 差异很大。本文所有示例都按 ScottPlot 5.x 编写,并固定使用本机已缓存的 5.1.58:
1 | <PackageReference Include="ScottPlot.WPF" Version="5.1.58" /> |
这样做的原因不是声称 5.1.58 是“2026 年最新版”,而是让文章、截图和示例代码有一致、可复现的基准。以后升级到新的 5.x 版本时,先看官方迁移说明,再修改项目文件。
ScottPlot 4 常见写法是:
1 | WpfPlot.Plot.AddScatter(xs, ys); |
ScottPlot 5 改为:
1 | WpfPlot.Plot.Add.Scatter(xs, ys); |
只差一个点,却会让旧教程里的代码完全无法编译。读到这里,先记住当前项目使用的是 Add.Scatter。
推荐学习顺序
- 先运行随附示例,依次点四个页签,只看每页显示什么。
- 第 1 章只做一张单曲线图,确认控件能正常出现。
- 第 2 章添加第二条曲线,并固定 Y 轴范围。
- 第 3 章把横轴换成时间,观察
ToOADate()前后的变化。 - 第 4 章修改刻度文字,再加一条 80 MPa 的预警线。
- 最后故意制造一次“有坐标轴没有曲线”,按排错图定位原因。
随附示例位置
1 | C:\Users\North\Desktop\WPF学习笔记\示例代码\WpfScottPlotDemo |
运行:
1 | cd "C:\Users\North\Desktop\WPF学习笔记\示例代码\WpfScottPlotDemo" |
如果本机可以直接访问 NuGet,也可以只执行:
1 | dotnet restore |
Visual Studio 中:
- 打开
WpfScottPlotDemo.csproj。 - 确认启动项目是
WpfScottPlotDemo。 - 平台选择
x64。 - 按
F5。 - 依次点击四个页签。
创建自己的跟做项目
先创建一个普通 WPF 项目:
1 | dotnet new wpf -n WpfScottPlotPractice -o .\WpfScottPlotPractice |
打开 WpfScottPlotPractice.csproj,先按下面的方式设置。这里使用 net462 和 x64,是为了避开“旧版 OpenTK 依赖已在本机缓存、新版依赖未缓存”的环境差异:
1 | <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> |
如果本机能正常联网还原 OpenTK 4.9.4 与 OpenTK.GLWpfControl 4.3.3,也可以把目标框架改成现代项目常用的:
1 | <TargetFramework>net9.0-windows</TargetFramework> |
两种目标框架下,本文的 ScottPlot 调用方式不变。不要一边用 ScottPlot 4 的 AddScatter,一边照抄本文的 Add.Scatter。
跟做完成后的结构
1 | WpfScottPlotPractice |
本文使用 WpfScottPlotDemo 作为示例命名空间。你自己练习时,可以把命名空间换成项目名,但 XAML 中的 x:Class、代码隐藏文件和命名空间必须一致。
遇到问题时先看这张图
图 2:控件空白、曲线空白、中文方框、修改后不刷新,原因分别在不同层。
1. ScottPlot图表库的引入
本节目标
完成本章后,你应该能:
- 在 WPF 项目中正确引入
ScottPlot.WPF。 - 在 XAML 中放置
WpfPlot控件。 - 用两个等长数组画出第一张折线图。
- 说清
WpfPlot与Plot的区别。 - 知道修改图表后为什么需要
Refresh()。
先理解职责分工
ScottPlot 最容易被误解的地方,是把 WpfPlot 当成“装数据的控件”。实际上它更像一个显示窗口:
1 | WPF 窗口 |
图 3:写代码时操作 Plot,显示时由 WpfPlot 承载。
记住这句话:
1 | 数据在 Plot 里,显示在 WpfPlot 上,刷新靠 Refresh。 |
第一步:引入命名空间
打开 MainWindow.xaml,在 Window 的开始标签中加入:
1 | xmlns:ScottPlot="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF" |
完整的窗口开始标签可以先写成:
1 | <Window x:Class="WpfScottPlotDemo.MainWindow" |
这里的 ScottPlot 是 XAML 前缀,不要求与类名完全相同。写成 sp 也可以,只要后面使用同一个前缀。
第二步:在 XAML 放置控件
在页面中放一个 WpfPlot,先给它一个名字:
1 | <Grid Margin="24"> |
x:Name="BasicPlot" 会在代码隐藏文件中生成同名字段,后面通过 BasicPlot.Plot 操作图表模型。
如果父容器没有可用宽高,控件可能显示成一条线或完全看不见。初学阶段先放进 Grid 的星号行或列:
1 | <Grid> |
第三步:准备等长的 X、Y 数组
新建 ChartSamples.cs。先只写一组数据:
1 | using System.Linq; |
Xs 与 Ys 都是 double[],并且长度都是 10。下标相同的两个值组成一个坐标点:
1 | (0, 24) (1, 31) (2, 38) ... (9, 84) |
不要使用“少一个也没关系”的想法。ScottPlot 添加散点序列时,两个数组必须一一对应;长度不一致时应先在业务代码里修正,而不是让图表去猜。
图 4:准备数据、添加序列、设置样式、固定坐标、刷新。
第四步:添加第一条序列
在 MainWindow.xaml.cs 中写入:
1 | using System.Windows; |
这段代码可以分成五件事:
BasicPlot.Plot:取得图表模型。plot.Clear():清掉上一次练习留下的内容。plot.Add.Scatter(...):用 X、Y 数组添加一条折线散点序列。Axes:给横纵轴写标题,并固定显示范围。BasicPlot.Refresh():请求 WPF 控件重新绘制。
第五步:认识 Series 返回值
plot.Add.Scatter 返回一个 Scatter 对象。常用设置如下:
| 属性 | 作用 | 示例 |
|---|---|---|
Color |
线条和点的主颜色 | Color.FromHex("#2456A6") |
LineWidth |
线宽 | 2.5f |
MarkerSize |
数据点标记大小 | 8 |
LegendText |
图例文字 | "压力" |
LinePattern |
实线、虚线等线型 | LinePattern.Dashed |
初学时不要一次把所有属性都改掉。先确认曲线出现,再逐项修改样式,才能知道是哪一个设置造成的变化。
第六步:处理中文字体
ScottPlot 的默认字体不一定包含中文字形。轴标题、图例中出现方框时,先设置字体:
1 | plot.Font.Set("Microsoft YaHei UI"); |
建议把它放进一个公共方法,避免每一页都重复:
1 | public static class ChartSamples |
调用顺序放在 Clear() 之后、添加序列之前:
1 | var plot = BasicPlot.Plot; |
如果系统没有安装微软雅黑,可以换成 "SimHei" 或你机器上确实存在的中文字体。字体名称写错时,图形不一定报错,但文字可能回退成默认字体。
运行结果
运行后应看到一张从约 24 MPa 上升到 84 MPa 的蓝色折线图,左轴固定为 0 到 100,底部轴显示采样序号。
图 5:随附示例第一页。图表、中文轴标题和图例都能正常显示。
跟做检查点
完成本章后逐项确认:
- 项目引用中出现
ScottPlot.WPF。 - XAML 能识别
ScottPlot:WpfPlot。 -
Xs.Length与Ys.Length相等。 - 代码中调用的是
plot.Add.Scatter,不是AddScatter。 - 修改模型后调用了
BasicPlot.Refresh()。 - 轴标题和图例中的中文不是方框。
- Y 轴范围固定为
0 - 100,而不是随数据自动变化。
常见错误
错误 1:提示找不到 WpfPlot
先检查 XAML 是否加入了:
1 | xmlns:ScottPlot="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF" |
再检查项目文件是否真的引用了 ScottPlot.WPF。命名空间正确、包没安装,仍然无法解析控件。
错误 2:图表区域是空白
按下面的顺序排查:
1 | WpfPlot 是否放在有实际宽高的容器中? |
错误 3:曲线出现了,但中文是方框
这是字体问题,不是数据问题。调用:
1 | plot.Font.Set("Microsoft YaHei UI"); |
错误 4:程序启动时报 DllNotFoundException: libSkiaSharp
ScottPlot 的绘图依赖 SkiaSharp 原生库。检查输出目录中是否存在 libSkiaSharp.dll,并确认进程位数与原生库一致。本文示例固定 win-x64,如果改用 Any CPU 后出现问题,先把运行环境明确为 x64。
小练习
把 Y 数据改成你自己熟悉的一组数值,例如一周七天的产量:
1 | var xs = new double[] { 1, 2, 3, 4, 5, 6, 7 }; |
然后完成三个小改动:
- 把 X 轴标题改为“星期”。
- 把 Y 轴标题改为“产量 / 件”。
- 把
MarkerSize改为0,观察折线是否仍然清楚。
本章小结
本章真正需要记住的不是某一行样式代码,而是这个调用链:
1 | 准备数据 → Add.Scatter → 设置 Axes → Refresh |
后面的多序列、时间轴和预警线,都是在这个链路上继续增加内容。
2. 数据序列与Y轴
本节目标
完成本章后,你应该能:
- 在同一个
Plot中添加多条数据序列。 - 用图例区分不同业务含义。
- 固定 Y 轴范围,避免刷新时坐标轴跳动。
- 判断两条数据是否适合画在同一个 Y 轴上。
一个 Plot 可以有多条序列
Plot 内部维护一个 PlottableList。每调用一次 Add.Scatter,列表中就多一个序列对象:
1 | Plot |
多条曲线共享同一个坐标系,所以它们的 X、Y 单位必须一致。进口压力和出口压力都可以用 MPa,因此可以共轴;温度与压力如果直接共用一个 Y 轴,即使线画出来了,读数也会误导人。
图 6:两条压力序列共用一个纵轴,纵轴范围固定为 0 到 100 MPa。
第一步:准备两组数据
在 ChartSamples.cs 中加入第二组 Y 值:
1 | public static double[] CreateSecondaryYs() |
仍然复用同一组 X 值。这样每个下标对应同一时刻或同一采样序号下的两个读数。
第二步:添加两条序列
1 | private void BuildSeriesChart() |
注意每个 Scatter 都有自己的 LegendText。如果两条线都不写图例文字,用户只能靠颜色猜哪条是进口、哪条是出口,这在工业界面中不合格。
第三步:理解固定 Y 轴
下面这行同时设置 X、Y 范围:
1 | plot.Axes.SetLimits(0, 9, 0, 100); |
也可以分开设置:
1 | plot.Axes.SetLimitsX(0, 9); |
固定 Y 轴有三个直接好处:
- 曲线更新时纵轴不跳,眼睛容易比较变化幅度。
- 同一设备不同批次的截图可以直接并排看。
- 预警线始终处在固定高度,不会因为自动缩放而看起来“时高时低”。
固定范围不是永远不调整。设备量程、单位或工艺阈值发生变化时,应当由业务规则明确更新范围,而不是让图表悄悄自动缩放。
第四步:更新数据并刷新
初学时可以先替换序列的数据源,再刷新:
1 | private void ReplaceSeriesData( |
真实实时曲线通常不会频繁删除并重建整个序列,而是更新数据源或追加点。这里先保留最直观的“替换 + 刷新”,等理解清楚后再优化。无论采用哪种方式,修改完成后都需要通知控件重绘。
运行结果
第二页应显示蓝色进口压力与橙色出口压力两条曲线,两条曲线共享 0 到 100 MPa 的纵轴。
图 7:随附示例第二页。图例明确区分两条序列,纵轴保持固定。
跟做检查点
- 两条
Scatter都添加到了同一个Plot。 - 两组 Y 数据长度都与 X 相同。
- 两条序列都设置了
LegendText。 -
SetLimitsY(0, 100)没有被后面的自动缩放覆盖。 - 点击“切换一组数据”后,纵轴仍然是 0 到 100。
- 点击“恢复初始数据”后,图例没有重复堆叠。
常见错误
错误 1:第二条线覆盖了第一条
如果两次添加数据时都调用了 plot.Clear(),第一次添加的内容会被清掉。一个页面只应在构建开始时清一次,然后依次添加所有序列。
错误 2:图例中出现两个相同的“压力”
LegendText 是显示名称,不是变量名。给两条线分别写成“进口压力”“出口压力”,而不是都保留默认或同名文字。
错误 3:固定范围后又调用自动缩放
如果后续代码调用 plot.Axes.AutoScale(),它会重新计算范围。固定范围之后,不要在同一刷新流程里又要求自动缩放。
错误 4:不同单位硬塞进一个 Y 轴
温度 ℃ 与压力 MPa 没有可比性。要么统一单位,要么使用额外坐标轴,并在界面中明确说明每条线对应哪一侧的轴。
小练习
把出口压力整体加上 5 MPa:
1 | var shifted = ChartSamples.CreateSecondaryYs() |
观察两条线的间距变化,再回答两个问题:
- 纵轴范围为什么仍然应该保持 0 到 100?
- 如果所有值都超过 100,界面应该提示什么,而不是悄悄把范围改成 0 到 120?
本章小结
多序列图的核心不是“多画几条线”,而是“让多条线在同一个可解释的坐标系里比较”。固定 Y 轴和图例文字,都是为了让比较有意义。
3. 基于时间的X轴标签
本节目标
完成本章后,你应该能:
- 把
DateTime转成 ScottPlot 能使用的 X 坐标。 - 使用
DateTimeTicksBottom()创建时间底部轴。 - 通过
DateTimeAutomatic.LabelFormatter修改时间标签。 - 区分“坐标值”和“显示文字”。
为什么不能直接把 DateTime 传给 Scatter
ScottPlot 的坐标计算使用 double。DateTime 是结构体,不能直接作为 X 数组元素交给 Add.Scatter。
常见做法是先转成 OLE Automation Date,也就是 OADate:
1 | var start = new DateTime(2026, 10, 23, 8, 0, 0); |
ToOADate() 把日期时间变成一个连续的 double,这样 ScottPlot 就能像处理普通数值一样处理时间间隔。
图 8:业务时间是 DateTime,坐标是 OADate,显示文字由刻度生成器还原。
第一步:准备时间与压力
1 | var start = new DateTime(2026, 10, 23, 8, 0, 0); |
这里每 10 分钟一个点,共 13 个点。时间数组和压力数组长度必须相同。
第二步:转换 X 坐标
1 | var xs = times |
转换后,xs 仍然是一组 double,但它代表的是时间。不要在这时用 Enumerable.Range 生成 0、1、2,否则横轴会变成序号,而不是真实时间。
第三步:换成时间底部轴
1 | var plot = TimePlot.Plot; |
DateTimeTicksBottom() 会移除原来的底部轴,创建一个日期时间轴,并把它加回图表。调用后,plot.Axes.Bottom.TickGenerator 应当是 DateTimeAutomatic。
第四步:修改时间标签格式
默认时间标签可能很长,也可能根据缩放级别自动变化。如果要固定显示“月-日 时:分”,先取出刻度生成器:
1 | if (plot.Axes.Bottom.TickGenerator |
这里委托的参数已经是 DateTime,不需要再调用 DateTime.FromOADate。不同版本的 API 可能对参数类型做过调整,所以写代码前先看 IDE 提示;本文按 5.1.58 的实际签名为准。
同一天内的数据,也可以只显示时分:
1 | ticks.LabelFormatter = date => |
跨天数据如果只显示 HH:mm,用户无法区分第二天和第一天的同一时刻。跨天时至少保留日期:
1 | ticks.LabelFormatter = date => |
第五步:观察时间轴
运行第三页,底部刻度应该显示类似:
1 | 10-23 08:00 |
而不是一串普通整数或很长的 OADate 数字。
图 9:随附示例第三页。底部轴已经按时间显示,而不是显示 OADate 原值。
跟做检查点
- X 数组是通过
ToOADate()生成的。 - 时间数组与 Y 数组长度相等。
- 调用了
DateTimeTicksBottom()。 - 底部刻度生成器是
DateTimeAutomatic。 - 切换格式后,曲线形状没有变化,只有标签文字变化。
- 跨天数据没有只显示时分。
常见错误
错误 1:X 轴显示 40000 多的大数字
这说明传给 Scatter 的确实是 OADate,但底部轴仍然是普通数值轴。补上:
1 | plot.Axes.DateTimeTicksBottom(); |
错误 2:时间标签全是同一个时刻
检查 times 是否真的在递增。可以在调试器中查看第一个和最后一个时间:
1 | Console.WriteLine(times.First()); |
错误 3:直接修改 xs ** 的格式字符串**
xs 是 double[],没有“时间格式”。时间格式属于刻度生成器的显示逻辑,不属于坐标数组本身。
错误 4:刷新后格式又被重置
如果每次刷新都重新调用 DateTimeTicksBottom(),之前设置的自定义格式可能被新的生成器覆盖。格式设置应放在轴创建之后,并避免在每次数据刷新时重复重建整个轴。
小练习
把采样间隔从 10 分钟改成 30 分钟:
1 | var times = Enumerable.Range(0, 13) |
再把格式改成:
1 | ticks.LabelFormatter = date => |
观察:在数据跨度变长后,刻度是否仍能清楚表达时间间隔?
本章小结
时间轴要分三层理解:
1 | DateTime:业务时间 |
只改最后一层,不会改变原始时间数据;忘记第二层,时间就无法进入图表。
4. 轴标签的其他格式与预警线
本节目标
完成本章后,你应该能:
- 使用
NumericAutomatic.LabelFormatter修改数值刻度文字。 - 给刻度补上单位,而不是把单位藏在说明文字里。
- 用
HorizontalLine添加一条独立预警线。 - 区分“数据序列”和“图表标注”。
- 完成一张带格式、单位和预警线的工业趋势图。
刻度文字是显示层
原始 Y 值可能是 80、85、90。用户看到的文字却可以是:
1 | 80.0 MPa |
数值没有变,变的是 LabelFormatter 生成的字符串。
图 10:原始数值、带单位显示和自定义逻辑之间的关系。
第一步:修改数值轴格式
1 | if (plot.Axes.Left.TickGenerator |
这里保留一位小数,并在后面补上 MPa。若只想显示整数:
1 | ticks.LabelFormatter = value => |
不要为了“看起来专业”给所有轴都加很多小数。刻度精度应该和业务测量精度一致。
第二步:修改 X 轴格式
示例中的 X 轴是运行小时数:
1 | if (plot.Axes.Bottom.TickGenerator |
如果要切换回无单位显示:
1 | ticks.LabelFormatter = value => |
轴的标题和刻度单位可以分工:
1 | 轴标题:运行时间 |
也可以把单位只放在轴标题中:
1 | 轴标题:运行时间 / h |
同一张图里选择一种写法并保持一致,不要一半写单位、一半不写。
第三步:添加预警线
预警线不是采样数据。它表达的是“超过这个值就进入警告状态”的业务规则:
1 | var alarmLine = plot.Add.HorizontalLine(80); |
HorizontalLine 是 Plottables 命名空间下的独立标注对象。它不会被追加进原始 ys 数组,也不会改变压力数据本身。
图 11:预警线固定在 80 MPa,原始曲线仍然按自己的采样值变化。
第四步:把数据、格式和预警线组合起来
1 | private void BuildAlarmChart() |
这段代码的顺序值得注意:
1 | 清空 → 字体 → 数据序列 → 预警线 → 轴标题 → 范围 → 标签格式 → 图例 → 刷新 |
顺序不是死规定,但先创建对象、再修改属性、最后刷新,读起来最清楚。
第五步:让预警线有业务含义
图表中的线只是视觉提示,真正的报警状态仍应由业务规则判断:
1 | public const double AlarmPressure = 80; |
不要把“线画在哪里”和“是否报警”写成两套互不相干的魔法数字。阈值应该来自同一份规则,界面和数据判断才能保持一致。
运行结果
第四页应显示蓝色压力曲线、红色虚线预警线和带单位的刻度标签。
图 12:随附示例第四页。X 轴显示小时,Y 轴保留一位小数和 MPa,80 MPa 预警线为红色虚线。
跟做检查点
- Y 轴刻度显示一位小数和 MPa。
- X 轴刻度显示小时。
- 预警线固定在 80 MPa。
- 预警线是虚线且颜色明显,但不抢过主曲线。
- 图例能区分“压力”和“预警线 80 MPa”。
- 修改格式后调用了
Refresh()。 - 原始压力数组中没有手动插入 80 这个“假数据点”。
常见错误
错误 1:把预警线放进 ys 数组
错误思路:
1 | var ys = new[] { 24, 31, 38, 80, 55, 61 }; |
这会把 80 当成某个时刻的真实采样值,曲线也会被强行拉到一个不存在的数据点。正确做法是使用 HorizontalLine。
错误 2:格式字符串没有生效
先确认刻度生成器确实是 NumericAutomatic。如果之前调用过 DateTimeTicksBottom(),底部轴生成器已经是时间类型,不能再按数值轴强转。
错误 3:轴标题写“压力”,刻度又写“MPa”,但单位不一致
例如轴标题写 kPa,刻度格式化却写 MPa。单位必须在数据、规则、轴标题和刻度之间保持一致。
错误 4:修改后没有刷新
1 | AlarmPlot.Refresh(); |
漏掉这一行时,模型已经改了,屏幕却还停留在上一次绘制结果。
小练习
把预警线阈值从 80 改成 75,并同步修改:
- 业务判断方法中的阈值。
- 预警线的 Y 值。
- 图例文字。
- 左侧轴刻度的显示格式。
然后增加一条 90 MPa 的红色点划线,观察两条预警线是否能清楚区分。
本章小结
一张可信的工业趋势图,至少要说清四件事:
1 | 画的是什么数据:序列与图例 |
格式只负责“怎么显示”,数据序列负责“真实值”,预警线负责“业务阈值”。三者分开,图表才容易维护。
完整跟做清单
项目文件
确认 WpfScottPlotDemo.csproj 至少包含:
1 | <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> |
四个页面的检查顺序
| 页签 | 应看到什么 | 关键 API |
|---|---|---|
| 1 引入与第一张图 | 一条蓝色压力曲线 | Add.Scatter、Refresh |
| 2 数据序列与 Y 轴 | 两条曲线共用固定纵轴 | 多次 Add.Scatter、SetLimitsY |
| 3 时间 X 轴 | 横轴显示日期时间 | ToOADate、DateTimeTicksBottom |
| 4 格式与预警线 | 带单位刻度、红色虚线 | NumericAutomatic.LabelFormatter、HorizontalLine |
自测命令
项目内包含一个不打开界面的自测入口:
1 | .\bin\Debug\net462\win-x64\WpfScottPlotDemo.exe --self-test |
自测结果会写入:
1 | bin\Debug\net462\win-x64\self-test-results.txt |
本次验证结果为 5/5 通过,覆盖:
- 第一张图已创建。
- X 与 Y 长度一致。
- 两条序列已创建。
- 时间轴生成器为
DateTimeAutomatic。 - 80 MPa 预警线存在且数量为一条。
截图命令
随附示例支持从程序内部截图,避免依赖当前桌面窗口是否被遮挡:
1 | .\bin\Debug\net462\win-x64\WpfScottPlotDemo.exe ` |
把 --screenshot-tab 改为 2、3、4,可以分别抓取另外三页。这个入口只用于验证与生成教程截图,不参与正常业务逻辑。
最后自检
- 四个页签都能打开。
- 每页图表都有真实曲线,不是空白。
- 中文轴标题、图例、状态文字都能显示。
- 第二页纵轴固定为 0 到 100。
- 第三页时间标签不是 OADate 原值。
- 第四页预警线位于 80 MPa。
- 修改图表后都调用了
Refresh()。 - 项目能够
dotnet build且无错误。
写在最后
ScottPlot 的入门门槛不高,但真正写出“可信的图”需要把四层分开:
1 | 数据层:xs、ys、DateTime、业务阈值 |
只要每加一个功能,都能说清它属于哪一层,图表出现空白、标签不对或预警线位置错误时,就不会再靠猜。
下一步可以继续练习:
- 把定时采集的数据追加到已有序列,观察实时曲线的滚动效果。
- 使用
plot.Axes.Right增加第二纵轴,比较不同单位的数据。 - 用业务规则计算预警状态,并在界面顶部同步显示报警文字。
- 把图表封装到用户控件中,让同一套趋势图复用到多个设备页面。
把这篇的四页示例完整跑通,再动手改数据、单位和阈值,你会比只看 API 列表更快建立自己的图表思维。

















