☰
LiveCharts2 坐标轴标签格式化(LabelsFormat):用 Labeler 与 Labels 定制 .NET 图表刻度文本
2026/10/6 2:26:59 网站建设 项目流程
  • 数据可视化
  • 图表库
  • 跨平台

【免费下载链接】LiveCharts2

Beautiful, interactive charts, maps, and gauges. One API for every .NET UI framework.

项目地址:https://gitcode.com/gh_mirrors/li/LiveCharts2
点击查看免费下载

导读

本篇文章围绕 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 theLabelsproperty 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 静态类中:

成员类型/签名说明
DefaultFunc<double, string>默认标签器,初始为Log10_6(见下);可用SetDefaultLabeler全局替换
SixRepresentativeDigitsFunc<double, string>即Log10_6,六位有效数字风格格式化
CurrencyFunc<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 的源码可以还原出完整调用链:

  1. 标签器解析:GetActualLabeler()判断Labels是否非空,非空则用BuildNamedLabeler(Labels)覆盖用户Labeler,否则使用Labeler属性(CoreAxis.cs);
  2. 尺寸预算:测量阶段通过GetPossibleMaxLabelSize()调用实际标签器,估算所有可能刻度的最大文本尺寸,用于轴布局(CoreAxis.cs);
  3. 刻度生成:绘制阶段对每个刻度位置调用TryGetLabelOrLogError(ctx.Labeler, i - 1d + 1d)生成标签文本,并把标签几何体加入LabelsPaint的绘制任务(CoreAxis.cs);
  4. 更新与绘制:数据变化时,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.

项目地址:https://gitcode.com/gh_mirrors/li/LiveCharts2
点击查看免费下载

相关推荐

上一篇:OpenSRE 调查流水线全解析:六阶段 RCA 流程从源码到图解一次看懂
下一篇:LMCache-mindspore架构详解:从原理到实践的完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询