- 数据可视化
- 图表库
- 跨平台
【免费下载链接】LiveCharts2
Beautiful, interactive charts, maps, and gauges. One API for every .NET UI framework.
导读
本篇文章围绕 LiveCharts2 官方示例 docs/samples/axes/labelsFormat 展开,系统讲解如何通过坐标轴Labeler委托与Labels集合自定义 Cartesian 图表的刻度标签文本:包括货币格式化、分类命名、全局默认标签器的替换,以及标签绘制的底层机制。阅读完本文后,你将掌握在 WPF、Avalonia、MAUI、WinForms、Blazor、Eto、Uno 等所有 .NET UI 框架下,用同一套 API 把纵轴刻度输出为$200、$400这类带单位文本,并让横轴显示 “Sergio / Lando / Lewis” 这类业务名称,同时理解这些配置在 CoreAxis 与 Labelers 中的真实生效链路。
示例整体结构:一个典型的 MVVM 标签格式化样本
labelsFormat是仓库中一个跨平台共享的示例。其文档主体由模板引擎渲染,把共享的 MVVM 视图模型与各平台视图拼装成文;示例源码则散落在各平台的Axes/LabelsFormat目录与共享的ViewModelsSamples中:
- 共享视图模型:samples/ViewModelsSamples/Axes/LabelsFormat/ViewModel.cs
- WPF 视图:samples/WPFSample/Axes/LabelsFormat/View.xaml
- Avalonia 视图:samples/AvaloniaSample/Axes/LabelsFormat/View.axaml
- MAUI 视图:samples/MauiSample/Axes/LabelsFormat/View.xaml
- Blazor 视图:samples/BlazorSample/Pages/Axes/LabelsFormat/View.razor
- Eto 视图:samples/EtoFormsSample/Axes/LabelsFormat/View.cs
- WinForms 视图:samples/WinFormsSample/Axes/LabelsFormat/Form1.cs
从仓库结构看,labelsFormat与其姊妹示例labelsFormat2(samples/ViewModelsSamples/Axes/LabelsFormat2/ViewModel.cs)共用同一套配置模式,区别仅在于分类名称(后者使用中文姓名),说明该格式化能力与具体语言环境无关,可直接复用到多语言业务场景。
视图模型:格式化逻辑与数据分离
示例采用 MVVM 模式,格式化逻辑全部封装在视图模型中,便于测试与跨平台复用:
using System; namespace ViewModelsSamples.Axes.LabelsFormat; public class ViewModel { public double[] Values1 { get; set; } = [426, 583, 104]; public double[] Values2 { get; set; } = [200, 558, 458]; public string[] Labels { get; set; } = ["Sergio", "Lando", "Lewis"]; public Func<double, string> Labeler { get; set; } = value => value.ToString("C2"); }这里演示了两个系列绑定到同一组横轴分类数据,关键点如下:
Values1/Values2:两组柱状系列数据,分别对应不同销售人员的销售额,绑定到XamlColumnSeries.Values;Labels:横轴分类名称集合,与数据点的整数位置一一对应,绑定到XamlAxis.Labels;Labeler:一个Func<double, string>委托,把刻度数值格式化为C2(货币、两位小数)字符串,绑定到纵轴XamlAxis.Labeler。
C2是 .NET 标准数字格式说明符中的货币格式,默认输出结果会带上当前线程CultureInfo的货币符号,例如$426.00;若要完全控制符号位置,可以使用value.ToString("C2", culture)指定区域性。关于Labeler与Labels的职责分工,官方文档 docs/cartesianChart/axes.md 有明确表述:
There are 2 ways to format and axis labels, using the
Labelsproperty and using theLabelerproperty, you must normally use theLabelsproperty to indicate names, and theLabelerproperty to give format to the current label.
视图层:XAML 中如何接线格式化配置
WPF 视图
WPF 视图把上述视图模型作为DataContext,通过绑定把数据与格式化配置注入图表:
<lvc:CartesianChart> <lvc:CartesianChart.Series> <lvc:SeriesCollection> <lvc:XamlColumnSeries Values="{Binding Values1}"/> <lvc:XamlColumnSeries Values="{Binding Values2}" Fill="{x:Null}"/> </lvc:SeriesCollection> </lvc:CartesianChart.Series> <lvc:CartesianChart.XAxes> <lvc:AxesCollection> <lvc:XamlAxis AxisName="Salesman/woman" Labels="{Binding Labels}"/> </lvc:AxesCollection> </lvc:CartesianChart.XAxes> <lvc:CartesianChart.YAxes> <lvc:AxesCollection> <lvc:XamlAxis AxisName="Sales" NamePadding="0,15" Labeler="{Binding Labeler}" LabelsPaint="{lvc:SolidColorPaint Color='#00f', FontFamily='Times New Roman', FontWeight=ExtraBold, FontWidth=Normal, FontSlant=Italic}"/> </lvc:AxesCollection> </lvc:CartesianChart.YAxes> </lvc:CartesianChart>各属性在示例中的作用:
AxisName="Salesman/woman":横轴名称,显示在轴末端;Labels="{Binding Labels}":横轴分类名称;AxisName="Sales"与NamePadding="0,15":纵轴名称及其与轴的距离;Labeler="{Binding Labeler}":纵轴刻度格式化委托;LabelsPaint:纵轴标签绘制笔刷,此处用SolidColorPaint指定蓝色(#00f)、Times New Roman 字体、ExtraBold 字重、Normal 字宽、Italic 斜体——这是 LiveCharts2 中统一控制轴标签视觉样式的入口。
从源码看,NamePadding、LabelsPaint等均定义于 CoreAxis 中,属于CoreAxis的可绑定属性;XamlAxis由代码生成器(generators/LiveChartsGenerators)从这些核心类型派生,因此在 XAML 中可直接书写。
Avalonia 视图的差异
Avalonia 版本的视图几乎相同,仅有两处框架相关差异:samples/AvaloniaSample/Axes/LabelsFormat/View.axaml 使用xmlns:lvc="using:LiveChartsCore.SkiaSharpView.Avalonia"引入命名空间,并把NamePadding写成NamePadding="{lvc:Padding '0,15' }"。Blazor 版本则使用 Razor 标记,逻辑完全一致(samples/BlazorSample/Pages/Axes/LabelsFormat/View.razor)。
Labels 与 Labeler:两种标签定制方式的职责边界
官方文档 Labels vs Labeler properties 将两种方式做了明确划分:
Labels(命名标签):IList<string>集合。当其非null时,轴的刻度文本将从该集合按整数索引取值——第 0 个数据点对应Labels[0],第 1 个数据点对应Labels[1],依此类推。若轴需要绘制超出集合边界的标签,则回退为直接显示索引值;默认值为null。Labeler(格式化委托):Func<double, string>,用于把刻度数值转换成任意字符串。它是“当前标签的格式化器”,适合货币、百分比、科学计数等数值格式。
两者的底层协作机制可以从 Labelers.BuildNamedLabeler 看出端倪:当Labels非空时,CoreAxis.GetActualLabeler 会优先用Labels构建命名标签器,覆盖用户设置的Labeler;而Labeler属性本身的默认值是Labelers.Default(CoreAxis.cs)。也就是说:命名标签的优先级高于格式化委托,两者分别服务于“分类名称”与“数值刻度”两种场景,这正与示例中“横轴用 Labels、纵轴用 Labeler”的用法一一对应。
从实现细节看,BuildNamedLabeler对越界索引与null条目都做了防御:索引小于 0 或超出集合长度时返回空字符串,集合内条目为null时同样返回空字符串(Labelers.cs)。这一点与文档“越界时回退显示索引”的描述略有出入,实际行为以源码为准,即越界返回空字符串而非索引值。
Labelers 静态工具类:官方内置格式化器
示例中的Labeler是手写的value => value.ToString("C2"),而 LiveCharts2 还提供了一组开箱即用的格式化器,全部位于 Labelers 静态类中:
| 成员 | 类型/签名 | 说明 |
|---|---|---|
Default | Func<double, string> | 默认标签器,初始为Log10_6(见下);可用SetDefaultLabeler全局替换 |
SixRepresentativeDigits | Func<double, string> | 即Log10_6,六位有效数字风格格式化 |
Currency | Func<double, string> | 货币格式化器,使用当前线程NumberFormatInfo.CurrentInfo.CurrencySymbol |
SetDefaultLabeler(labeler) | void | 把Default替换为自定义委托 |
FormatCurrency(value, thousands, decimals, symbol) | string | 底层货币格式化实现,支持 K/M/B/T 缩写的自定义版本 |
BuildNamedLabeler(labels) | Func<double, string> | 用IList<string>构建按索引取名的标签器 |
内置货币格式化器的“缩写”行为
官方文档对Labelers.Currency有专门说明(docs/cartesianChart/axes.md#labels-vs-labeler-properties):它比手写value.ToString("C")更聪明——当数值达到百万、十亿、万亿量级时,会输出更短的标签。其实现逻辑在 FormatCurrency:
- 对
value取以 10 为底的对数判断量级; 10^6 ≤ value < 10^9:除以10^6,后缀M(百万);10^9 ≤ value < 10^12:除以10^9,后缀B(十亿);10^12 ≤ value < 10^15:除以10^12,后缀T(万亿);value ≥ 10^15:除以10^15,后缀Q(千万亿)。
因此,当销售总额高达数百万时,Labelers.Currency会把1,234,567渲染成$1.23M之类的短标签,避免轴标签拥挤。
默认标签器的数字简化行为
Default(即Log10_6)同样会对极大/极小值做缩写:value ≥ 10^6时除以百万并追加M后缀,value ≤ 10^-6时乘以百万并追加µ后缀,其余情况保留六位小数(Labelers.cs)。这意味着即使你不配置任何格式化器,LiveCharts2 也会自动把超大数值的轴标签压缩成易读形式。
底层渲染链路:标签如何被绘制到画布
了解格式化配置的底层生效路径,能帮助你判断什么场景该用哪个属性。从 CoreAxis 的源码可以还原出完整调用链:
- 标签器解析:
GetActualLabeler()判断Labels是否非空,非空则用BuildNamedLabeler(Labels)覆盖用户Labeler,否则使用Labeler属性(CoreAxis.cs); - 尺寸预算:测量阶段通过
GetPossibleMaxLabelSize()调用实际标签器,估算所有可能刻度的最大文本尺寸,用于轴布局(CoreAxis.cs); - 刻度生成:绘制阶段对每个刻度位置调用
TryGetLabelOrLogError(ctx.Labeler, i - 1d + 1d)生成标签文本,并把标签几何体加入LabelsPaint的绘制任务(CoreAxis.cs); - 更新与绘制:数据变化时,
UpdateLabel会按相同标签器刷新文本,最后通过canvas.AddDrawableTask(LabelsPaint, zone: CanvasZone.NoClip)提交到画布(CoreAxis.cs)。
其中TryGetLabelOrLogError说明:若格式化委托抛出异常,LiveCharts2 会记录日志而不是让整个渲染流程崩溃,这在自定义复杂Labeler时能显著提升健壮性。
从测试角度看,仓库测试中也有对标签与格式化行为的覆盖(tests/CoreTests/OtherTests 与 tests/CoreTests/ChartTests 中的轴相关用例),可作为深入验证行为时的参考入口。
全局替换默认标签器
如果你希望整个应用中所有轴的默认刻度格式统一,无需逐个轴配置Labeler,直接调用静态方法即可:
Labelers.SetDefaultLabeler(value => value.ToString("N0"));SetDefaultLabeler会修改Labelers.Default静态属性(Labelers.cs),而CoreAxis.Labeler的初始值正是Labelers.Default,因此新创建的所有轴都会自动采用新格式。注意:该设置是全局静态的,会作用于之后创建的每个图表实例,适合放在应用初始化阶段(如App.xaml.cs或Program.cs)执行。
应用场景与注意事项
- 业务分类标签:当数据点是枚举类名称(人名、月份、地区)时,优先使用
Labels,配合LabelsRotation(如 45 度)解决长名称拥挤问题(参考 docs/cartesianChart/axes.md); - 数值刻度美化:金额用
Labelers.Currency或自定义货币委托,百分比用value => $"{value:P0}",超大数值依赖默认Log10_6的M/µ缩写; - 样式统一:轴标签的字体、颜色、字重统一通过
LabelsPaint配置,跨框架 API 一致; - 优先级规则:
Labels非空时优先于Labeler,二者不要同时承担同一根轴的文本职责; - 多语言:
labelsFormat2示例(ViewModelsSamples/Axes/LabelsFormat2/ViewModel.cs)使用中文姓名验证了该机制与语言无关,分类名可直接来源于业务数据(如数据库中的姓名列)。
小结
通过 labelsFormat 示例,本文完整梳理了 LiveCharts2 坐标轴标签格式化的两大入口:Labels负责按索引映射分类名称,Labeler(类型为Func<double, string>)负责把刻度数值转换为任意文本;同时介绍了Labelers工具类提供的Default、Currency、BuildNamedLabeler等内置格式化器及其M/B/T/Q缩写逻辑,并深入 CoreAxis 源码还原了“标签器解析 → 尺寸预算 → 刻度生成 → 绘制任务提交”的完整链路。这套能力在 WPF、Avalonia、MAUI、WinForms、Blazor、Eto、Uno 等所有 .NET UI 框架中共享同一套 API 与实现,真正做到“一处学会,处处可用”。
- 数据可视化
- 图表库
- 跨平台
【免费下载链接】LiveCharts2
Beautiful, interactive charts, maps, and gauges. One API for every .NET UI framework.
相关推荐
PHPExcel图表坐标轴设置:刻度与标签自定义终极指南
PHPExcel图表坐标轴设置:刻度与标签自定义终极指南 想要创建专业级的Excel图表?掌握PHPExcel图表坐标轴设置技巧是关键!🎯 本指南将带你深入了
后端数据处理Chart.js坐标轴定制:刻度、标签与网格线配置完整指南
Chart.js坐标轴定制:刻度、标签与网格线配置完整指南 Chart.js是一个功能强大的开源JavaScript图表库,让开发者能够轻松创建美观的交互式图表
图表库前端数据可视化Flet Charts ChartAxisLabel 详解:为坐标轴指定刻度定制专属标签
Flet Charts ChartAxisLabel 详解:为坐标轴指定刻度定制专属标签 导读 本文围绕 Flet Charts( flet charts )中
前端跨平台桌面应用移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考