1. 项目概述与整体选型思路
1.1 核心场景:谁需要LiveCharts,它解决什么痛点
在实际开发中,尤其是桌面端工具类应用,几乎避不开一个需求:把数据可视化。不管是设备监控、数据采集分析,还是后台管理面板,都需要用图表把数字变成直观的曲线和柱状图。早些年大家习惯用微软自带的Chart控件,功能确实完整,但用起来相当繁琐——需要手动管理ChartArea、Series、Points三个层级,数据绑定要写大量模板代码,样式调整更是牵一发动全身。后来我接触到LiveCharts这个组件库,才发现原来在C#里画图表可以这么省心。
LiveCharts是一款面向.NET生态的免费开源图表库,支持WPF、WinForms、UWP、Avalonia等多个UI框架。它的核心卖点就四个字:简单、灵活。简单体现在API设计上,创建一条折线图只需要几行代码,数据源直接绑定集合对象即可;灵活则体现在它基于MVVM的设计哲学,图表配置本身就是一堆可观察对象,你可以像操作普通C#属性一样动态修改坐标轴、系列样式甚至动画行为。
这个库最适合两类人:一类是做桌面工具软件的开发者,需要快速集成图表功能,不想在图表组件上耗费太多时间;另一类是做过WPF开发、想寻找更优雅数据可视化方案的入门者。它同样适合正在做工业监控、金融行情显示、科研数据绘图等场景的技术人员,因为这些领域的共同特点是数据实时性要求高、界面需要频繁刷新,而LiveCharts在这方面有专门优化。
1.2 版本选型:经典版与LiveCharts2的取舍
在选择LiveCharts之前,必须搞清楚一个关键问题:这个库目前实际分为两个大版本,它们的API体系完全不兼容。
经典版是0.x系列,也就是GitHub上最常见的那个版本,稳定、资料多、网上示例遍地都是,但官方已经停止新功能维护。它基于WPF的Canvas绘制,单条曲线实时刷新性能尚可,但多个图表同时大屏显示会明显卡顿。
LiveCharts2是后来重写的全新架构,支持SkiaSharp绘图引擎,跨平台性能更好,动画也更流畅,API设计上更现代。但它的学习曲线稍微陡峭一些,对初学者来说,一些概念(比如定位、绘制顺序)需要重新理解。
我个人的建议是:如果你维护的是老项目,或依赖大量网上现成示例,选经典版更稳妥;如果你是全新项目,且对性能有较高追求,优先考虑LiveCharts2。这篇博文的实战部分,我以经典版为主来演示——因为它的示例代码存量最大,遇到的坑也很典型,把这些经验讲透了,你换成LiveCharts2也能触类旁通。
2. 环境搭建与第一张图表的诞生
2.1 NuGet包安装与引用细节
新建一个WPF项目后,安装LiveCharts分两种情况。如果你只做WPF开发,安装LiveCharts.Wpf这个包就够了,它会自动带上核心依赖LiveCharts;如果还要支持WinForms,就再装一个LiveCharts.WinForms。
从NuGet装包没有什么难度,但有几个细节会影响你后续的使用体验。首先是版本锁定——装完包之后,建议在项目文件里确认一下引用版本,避免误升级到LiveCharts2导致API不兼容。其次是XAML命名空间,需要在窗口或用户控件的根元素上添加:
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"这样你就可以在XAML里声明lvc:CartesianChart作为图表容器。我还习惯把主题资源加到App.xaml里,这样全局图表样式统一:
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/LiveCharts.Wpf;component/Themes/Base.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources>如果不加这一步,图表虽然能显示,但默认样式比较粗糙,后续调样式时容易出现资源找不到的异常。
2.2 一个最小可运行的折线图
光说不练容易空。我们先从最简单的场景入手:展示一周的销售数据。在后台代码里定义模型和数据集合:
public class SalesData { public string Week { get; set; } public double Amount { get; set; } } // 准备数据 var data = new List<SalesData> { new SalesData { Week = "周一", Amount = 120 }, new SalesData { Week = "周二", Amount = 200 }, new SalesData { Week = "周三", Amount = 150 }, new SalesData { Week = "周四", Amount = 98 }, new SalesData { Week = "周五", Amount = 320 }, new SalesData { Week = "周六", Amount = 410 }, new SalesData { Week = "周日", Amount = 280 } };然后在XAML中声明一个折线系列:
<lvc:CartesianChart x:Name="MainChart"> <lvc:CartesianChart.Series> <lvc:LineSeries Title="销售额" Values="{Binding SalesValues}" Stroke="#FF5A5A" FillTransparency="0.3" StrokeThickness="2" PointGeometrySize="8"/> </lvc:CartesianChart.Series> </lvc:CartesianChart>在ViewModel或Code-Behind里,把SalesValues绑定为一个ChartValues<double>集合,仅需将原始数据中的Amount值填进去即可:
SalesValues = new ChartValues<double>(data.Select(d => d.Amount));运行程序,一条折线就渲染出来了。这个过程中你几乎不用关心坐标轴范围、刻度计算这些底层细节,LiveCharts会根据数据自动推算。这种“拿来即用”的体验,就是它相比传统Chart控件的核心优势。
2.3 图表容器选择:CartesianChart与PieChart的分工
LiveCharts里有两类核心图表容器:CartesianChart用于直角坐标系图表,例如折线图、柱状图、散点图;PieChart用于饼图、环形图。每一类容器有独立的系列类型,不能混用。比如你在CartesianChart里放PieSeries,运行时会直接报错。
我见过不少新手在这个地方犯晕,原因在于把“图表类型”和“容器类型”搞混了。记住一句话:先选容器,再选系列。容器决定了坐标系,系列决定了数据呈现形态。在WPF里这个逻辑必须在XAML结构上保持一致,这一点比在代码里直接new对象要严格得多。
3. 核心特性深入:Series、Axes、Mappers
3.1 Series系列类型与常用配置项
LiveCharts的Series是整个图表库的视觉核心。掌握它,你就掌握了大半个库。
常用的Series类型如下表所示:
| 系列类型 | 适用场景 | 关键属性 | 常见坑点 |
|---|---|---|---|
| LineSeries | 趋势分析、连续数据 | Stroke, StrokeThickness, PointGeometry | 默认自动生成点标记,数据量大时建议关闭 |
| ColumnSeries | 分类对比 | Fill, MaxColumnWidth, ColumnPadding | 柱子宽度设置不当会产生重叠或过窄 |
| StepLineSeries | 阶梯型变化数据 | StepPattern | 注意步长方向设置,否则图形反向 |
| ScatterSeries | 散点分布 | MaxPointShapeDiameter | 点大小默认一样,需要设置直径范围 |
| PieSeries | 占比构成 | PushOut, Sector | 饼图扇区最小角度限制,太小的数据可能被忽略 |
以LineSeries为例,有几个细节值得展开。PointGeometry的默认值是一个圆形,表示每个数据点上的标记。如果你数据点很多(比如上千个),这些圆形标记会成为渲染负担,此时可以把PointGeometry设为null来关闭。Fill属性控制折线下方区域的填充色,FillTransparency控制透明度,对于折线图来说,透明度通常设置在0.1到0.4之间比较美观,太高显得脏,太低又遮住网格线。
ColumnSeries的MaxColumnWidth是最容易困惑的属性。它表示柱子的最大像素宽度,如果不设置,LiveCharts会根据分类数量自动计算宽度。当你发现柱子挤在一起时,把它调小;当你发现柱子宽得像块砖头时,把它调大。ColumnPadding控制柱子间距,它在0到1之间,实际表现是比例的百分比值,这个细节很容易被忽略,但也正是调整柱状图颜值的关键。
3.2 坐标轴配置的艺术:从默认到精准控制
默认情况下,LiveCharts会自动计算坐标轴的范围和步长,但业务需求往往不满足于默认。比如你有两个系列,一个数值范围在0到100,另一个在1000到10000,如果放在同一个Y轴上,小数值的线条会被压成一条直线。这时候就需要双坐标轴。
在XAML里,CartesianChart的AxisX和AxisY都支持集合,你可以添加多个坐标轴。每个Series通过属性ScalesXAt和ScalesYAt指定使用哪个坐标轴,对应关系靠索引绑定:
<lvc:CartesianChart.AxisY> <lvc:Axis Title="数值A" MinValue="0" MaxValue="100" /> <lvc:Axis Title="数值B" MinValue="0" MaxValue="10000" /> </lvc:CartesianChart.AxisY>第一个系列默认挂在索引0的Y轴上,第二个系列用ScalesYAt="1"挂到索引1上。这里有个容易踩的坑:坐标轴的索引是全局的,不是单纯按类型区分的。你新增一个X轴或Y轴,它们的索引都是独立计数的。写错索引通常不会报错,但图表会莫名其妙的空白或不显示某些系列。
再说刻度格式化。当你展示大数值(比如销售额35600)时,坐标轴标签显示“35600”显得很长,如果能显示成“3.56万”会更友好。LiveCharts通过LabelFormatter实现:
Axis.LabelFormatter = value => (value / 10000).ToString("0.00") + "万";需要注意,坐标轴的格式化函数会在每次重绘时被大量调用。我遇到过在格式化函数里做复杂计算导致界面卡顿的情况,所以尽量保证这个函数内部逻辑简单,只做字符串格式化,不要做数据库查询或集合遍历。
3.3 数据绑定与Mappers:对象到坐标的桥梁
LiveCharts的Values属性要求的是ChartValues<T>集合,T一般可以是double、decimal、int等值类型,也可以是一些坐标结构。但实际项目中,你手上的数据往往是一个包含多个属性的业务对象。这时候就需要Mapper来告诉LiveCharts:“从对象的哪个属性取X值,从哪个属性取Y值。”
一个经典场景:你有一个传感器数据对象SensorRecord,包含Timestamp和Temperature两个属性。如果你直接把这个对象列表传给ChartValues<SensorRecord>,然后测试运行,会发现图表什么也不显示。原因就是LiveCharts不知道该怎么把这个对象变成坐标点。
解决办法是使用SeriesCollection配合ChartValues<T>的Map方法:
var values = new ChartValues<SensorRecord>(sensorData); values.Map(record => { record.X = record.Timestamp.ToOADate(); // 时间转数值 record.Y = record.Temperature; });这里的X和Y不是SensorRecord上真实存在的属性,而是LiveCharts提供的ChartPoint的映射位置。由于C#不能动态添加属性,常见做法是让业务对象继承ObservableObject并额外声明X、Y两个可供绑定的属性:
public class SensorRecord : ObservableObject { public DateTime Timestamp { get; set; } public double Temperature { get; set; } public double X { get; set; } public double Y { get; set; } }这个映射过程是LiveCharts里数据处理的关键中转站。理解了它,你就不会再为“为什么我的对象数据画不出来”而困扰。
4. 实战案例:销售数据看板完整实现
4.1 综合图表布局设计
有了前面的基础,我们来做一个稍微完整一点的项目:一个销售数据看板,包含折线图、柱状图、饼图三种图表形态,用于展示某区域的周销售趋势、品类销量对比和渠道占比。这个看板做出来以后,可以直接套用到大多数后台管理工具里。
我先把整体的界面布局列在下面:
<Grid Margin="12"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="销售数据看板" Grid.ColumnSpan="2" FontSize="20" FontWeight="Bold" Margin="0,0,0,12"/> <lvc:CartesianChart Grid.Row="1" Grid.Column="0" x:Name="TrendChart"/> <lvc:CartesianChart Grid.Row="1" Grid.Column="1" x:Name="CategoryChart"/> <lvc:PieChart Grid.Row="2" Grid.Column="0" x:Name="ChannelChart"/> </Grid>三个图表各司其职:TrendChart展示7天销售额折线,CategoryChart展示各类目销售柱状图,ChannelChart展示线上线下的渠道占比饼图。
4.2 折线图:趋势分析实现
折线图的数据我模拟生成,实际项目里从数据库读取即可:
private void LoadTrendData() { var trendValues = new ChartValues<double> { 120, 135, 98, 200, 260, 310, 285 }; var trendSeries = new LineSeries { Title = "本周销售额", Values = trendValues, Fill = System.Windows.Media.Brushes.Transparent, StrokeThickness = 2, PointGeometrySize = 6 }; TrendChart.Series = new SeriesCollection { trendSeries }; TrendChart.AxisX.Add(new LiveCharts.Wpf.Axis { Title = "星期", Labels = new[] { "周一", "周二", "周三", "周四", "周五", "周六", "周日" } }); TrendChart.AxisY.Add(new LiveCharts.Wpf.Axis { Title = "金额", LabelFormatter = value => value.ToString("C0") }); }这里的关键是Axis.Labels属性,它提供分类轴标签。为了让标签和Values的数据点一一对应,Labels数组的长度必须与Values的长度一致,否则会出现错位。我原来在项目中因为数组多了一个空字符串,导致整个坐标轴标签后移一位,排查了好一阵子。
另一个值得说明的点是LabelFormatter里使用了ToString("C0"),这会把数值格式化为货币样式,比如120显示为“$120”。如果你在中国业务环境,可以用value.ToString("¥#,##0")或者自己拼接。
4.3 柱状图:品类对比与颜色配置
柱状图要表现各品类的销量,如果还是用单色会很单调,可以给每个柱子单独设置颜色。LiveCharts的ColumnSeries在默认情况下所有柱子共用一个Fill,但如果为每个数据点提供一个自定义的SolidColorBrush值,柱状图会保持值类型为SolidColorBrush的集合,并自动按每个数据点着色。
var categoryValues = new ChartValues<double> { 560, 340, 425, 280, 190 }; var columnSeries = new ColumnSeries { Title = "品类销量", Values = categoryValues, Fill = System.Windows.Media.Brushes.SteelBlue, MaxColumnWidth = 35 }; CategoryChart.Series = new SeriesCollection { columnSeries }; CategoryChart.AxisX.Add(new LiveCharts.Wpf.Axis { Labels = new[] { "手机数码", "家电", "服饰", "食品", "图书" } });柱状图的宽度和间距我调过很多次,最终发现MaxColumnWidth设置为30到40之间视觉效果比较均衡。太宽显得笨重,太窄显得单薄。ColumnPadding默认是0.2,如果你有多个系列并列,会看到柱组之间留有一定空隙,这是预期行为,不用额外设置。
4.4 饼图:渠道占比与扇区联动
饼图在LiveCharts里相对简单,不需要坐标轴,只要把数据塞进PieSeries即可:
var channelSeries = new PieSeries { Title = "线上渠道", Values = new ChartValues<double> { 62 }, Fill = System.Windows.Media.Brushes.CornflowerBlue }; var channelSeries2 = new PieSeries { Title = "线下渠道", Values = new ChartValues<double> { 38 }, Fill = System.Windows.Media.Brushes.IndianRed }; ChannelChart.Series = new SeriesCollection { channelSeries, channelSeries2 };这里有个很反直觉的设计:饼图的每个扇区是一个独立的PieSeries,而不是在一个PieSeries里放多个值。刚开始用的时候,我习惯性地创建一个PieSeries然后把所有百分比都丢进去,结果整个饼图只有一个扇区,怎么想都不对。后来才明白LiveCharts的系列设计就是这个逻辑:一个PieSeries就是一块饼的切片,多个PieSeries拼成一个完整的饼。
饼图默认的起始角度是0度(即3点钟方向),如果需要调整可以用StartingRotationAngle属性。我一般不做调整,除非有特别的设计需求。
4.5 布局拼接与整体效果验证
把三块图表拼装好后,剩下的是处理一些细节:柱状图与折线图的标题字体大小、坐标轴网格线颜色、Margin间距等。LiveCharts的默认主题偏深色,如果你的看板背景是浅色,记得把图表容器的Background属性改成透明或白色。
运行这个看板后,应当看到三个图表清晰展示各自的业务含义。到这里,你已经掌握了一个完整的静态数据可视化流程。但真实业务里,还有更刚性的需求——实时数据更新。下面我们来攻克这个难点。
5. 实时动态数据更新:不只是刷新集合
5.1 模拟传感器数据源:定时器驱动
很多监控类软件需要图表每秒甚至每毫秒接收新数据。如果每次收到数据都重新赋值整个ChartValues集合,会造成界面卡顿和闪烁。正确做法是增量更新,用定时器模拟一个高频数据源:
private readonly Random _random = new Random(); private readonly ChartValues<double> _liveValues = new ChartValues<double>(); private DispatcherTimer _timer; private void StartLiveUpdates() { _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) }; _timer.Tick += (sender, args) => { _liveValues.Add(_random.Next(0, 100)); if (_liveValues.Count > 60) // 只保留最近60个点 { _liveValues.RemoveAt(0); } }; _timer.Start(); }这里我用DispatcherTimer而不是普通System.Timers.Timer,原因是WPF的UI操作必须在主线程中执行。直接使用后台线程定时器去修改ChartValues集合,会抛出跨线程访问异常。如果你需要在后台线程产生数据,那就要用Application.Current.Dispatcher.Invoke把更新切换到UI线程。
5.2 必然踩坑:不能修改正在被迭代的集合
上面代码里有一个Star问题:_liveValues在UI线程里被定时器修改,而LiveCharts的绘图引擎可能同时正在读取这个集合。如果正好在迭代过程中执行RemoveAt,就会触发“集合已被修改;无法执行枚举操作”的异常。这是实时图表开发中最高频的报错,没有之一。
解决方案有三种,我按推荐程度排序:
- 使用
ObservableCollection<ChartPoint>配合映射,但这比较麻烦,要额外处理映射。 - 用
ChartValues<double>时,在更新前锁定一个对象引用,每次更新先clear再AddRange。但clear操作会导致坐标轴闪烁。 - 使用LiveCharts官方提供的
LiveCharts.Helpers里的LiveChartValues类型,它是线程安全的集合。
实际上有很多人推荐用第3种方案,但我实测后发现它依然需要小心处理UI线程切换。最稳妥的方案还是:控制好更新频率,一次定时器Tick只做一次Add和RemoveAt,不要在循环里批量修改。
5.3 性能优化:数据点数与坐标轴范围的平衡
实时图表最容易出现的问题,是数据点越积越多,渲染越来越卡。这是Canvas绘制机制决定的——每条线、每个标记都是独立的绘制元素,数据量一大,CPU和内存占用自然上涨。
我的做法是固定窗口长度。比如最多保留60个点,每次新点进来,同时移除最旧的点。这样图表永远只有60个线段,渲染压力是恒定的。你还可以配合Axis.MinValue和Axis.MaxValue来固定Y轴范围,避免取值范围反复跳动导致视觉上的不断缩放:
LiveChart.AxisY[0].MinValue = 0; LiveChart.AxisY[0].MaxValue = 100;如果不固定范围,图表会在数据波动时不断调整坐标轴刻度,视觉上看起来很“抖动”。但要注意,如果你不确定后续数据会处于什么范围,先不要固定死坐标轴,否则一旦超出范围,曲线会直接冲出画面外。
5.4 大数据量场景的终极方案:跳过动画
LiveCharts为了让图表看起来更生动,默认所有系列都有动画过渡效果。这在数据量小的时候是加分项,数据量大时则是性能杀手。因为每个动画帧都要重新计算插值。
在实时高频更新的场景下,我强烈建议关闭动画:
LiveChart.DisableAnimations = true;这个属性可以在XAML中直接设置,也可以在后台代码中设置。关闭动画后,数据更新速度会提升一个数量级。如果你还想保留一定的视觉效果,可以关闭部分动画而不是全部,但说实话,监控类工具没有人会盯着图表动画看,稳定的实时数据比花哨的效果重要得多。
6. 常见问题与排查技巧实录
6.1 图表空白:数据明明有,页面什么都看不到
这个问题我遇到过三次,三次原因都不同。第一次是数据源绑定了空集合;第二次是坐标轴范围错了,数据值远在MinValue和MaxValue范围之外;第三次是Series的Values属性没有正确绑定到ChartValues,而是绑定成了List 。
排查顺序很重要。我建议打开调试器,先看Series的Values.Count是否大于0,再看SeriesCollection是否被正确赋给图表容器。如果这两步正常,检查图表的ActualWidth和ActualHeight是否为0——有些情况下图表放在未展开的容器里,暂时无法渲染。最后再检查坐标轴范围。
6.2 中文标签显示乱码或方块
LiveCharts默认字体对中文支持不算好,在坐标轴或Legend里显示中文时,偶尔出现方框。这个问题通常不是LiveCharts本身的问题,而是WPF默认字体没有匹配到中文字体。解决方案有两个:一是在坐标轴实例上设置FontFamily属性;二是在窗口级别统一设置FontFamily:
<Window FontFamily="Microsoft YaHei UI, SimHei, sans-serif">我习惯在窗口根元素设置,因为这样所有图表组件都会继承中文字体,不用逐个调整。
6.3 柱状图柱子“挤压”在一起不美观
当分类数量多且每个分类值差异大时,柱子会自动压缩宽度。曾有一个项目,分类数量有40多个,柱子变成了一根根细线。解决办法是不要水平排列所有柱子,改用纵向布局的RowSeries或者直接用折线图。如果你仍然要柱状图,可以给MaxColumnWidth设置一个最小阈值,但依然无法解决根本问题——分类太多,空间有限。
6.4 多系列图表中某个系列的颜色总是自动变化
LiveCharts的系列颜色默认取自主题色板,如果你没有显式设置Fill或Stroke,颜色可能会随着系列数量自动循环。很多情况下这不是bug,而是主题的行为。如果你想要稳定颜色,必须显式设置每个系列的Fill或Stroke。尤其在动态添加删除系列时,颜色序号会被重排,导致视觉上颜色跳动。
6.5 工具提示(Tooltip)不显示
Tooltip不显示通常是因为图表容器被设置了IsHitTestVisible="False",或者鼠标事件被上层控件拦截。排查时先确认图表区域是否能正常接收鼠标事件,然后在Series上设置Tooltip属性,例如:
series.Tooltip = new DefaultTooltip { SelectionMode = TooltipSelectionMode.SharedY };还有一点容易忽略:如果你配置了自定义的坐标轴LabelFormatter,请确保它返回的字符串不包含特殊字符,否则可能影响Tooltip中显示的格式。
6.6 实时更新导致UI卡顿的优化清单
实时更新的卡顿问题,大部分不是LiveCharts的锅,而是你的更新策略不合理。我整理一个排查清单:
| 排查项 | 判断标准 | 优化建议 |
|---|---|---|
| 数据点数 | 是否保持在100点以内 | 超出后移除最旧数据 |
| 更新频率 | 是否每秒超过10次 | 降低到5-8次可感知的流畅度 |
| 动画 | 是否禁用 | 实时场景必须禁用 |
| 坐标轴范围 | 是否自动计算 | 固定MinValue/MaxValue |
| ViewModel通知 | 高频属性是否触发了多余通知 | 批量更新后再触发PropertyChanged |
这五个维度依次排查,90%的卡顿问题就能解决。剩下的10%可能来自电脑性能本身,或者使用了过于复杂的自定义图例。
6.7 一个容易被忽略的问题:DPI缩放导致图表模糊
在Windows的高DPI显示设置下,LiveCharts经典版的图表可能出现文字模糊、线条发虚。这是因为经典版基于Canvas绘制,在高DPI缩放时没有自动适配。解决方法是让图表所在窗口启用PerMonitorDpi Aware。不过更省事的做法是在工程里逐窗口设置:
<Window ... UseLayoutRounding="True" SnapsToDevicePixels="True">这个设置能有效缓解模糊问题,但不能完全根治。如果项目对高清屏适配要求极高,可以考虑迁移到LiveCharts2,它对DPI的处理要完善得多。
7. 实战经验总结:从能用到好用
整套LiveCharts用下来,我最大的感触是:它确实把图表开发的复杂度降下来了,但要真正用得顺手,还是得花时间理解它的坐标映射模型、系列容器的分工,以及更新时序。
如果你是从零开始,建议按这个路径学习:先照着官方示例画一个静态柱状图,再扩展成一个折线图加柱状图的组合图表,然后尝试用业务对象做Mapper映射,最后再碰实时刷新。不要一上来就做复杂的动态大屏,否则你会同时踩中数据绑定、线程更新、性能优化三组坑,很难分清问题出在哪。
我自己在项目中经常同时打开LiveCharts的官方示例代码和源码。因为有些奇怪的渲染问题,看文档根本看不出原因,直接搜源码里的实现反而更快。比如柱状图宽度计算的问题,我就是在源码里看到ColumnPadding的乘以某个比例系数之后才彻底弄明白。
最后分享一个小技巧:在调试图表的ViewModel时,给坐标轴和Series设置Name属性。LiveCharts在后台报错时,异常信息会带上这个Name,你能很快定位到是哪个图表实例出了问题。单图表场景无所谓,图表数量一多,这个习惯能帮你省下大量排查时间。
如果你准备把LiveCharts用进正式项目,请记住:先确认目标平台是否在支持列表里,锁定版本号,然后写一个小的技术验证Demo跑通核心功能,再往里面填业务逻辑。组件库再好,也只是工具,真正的价值还是在数据分析和业务呈现上多花心思。