在 .NET 中管理演示文稿的图表数据标签
简介
数据标签显示有关图表系列和各个数据点的信息,帮助读者识别数值并理解图表。本文解释如何格式化数值、显示百分比、读取标签文本、控制超出坐标轴最大值的标签、调整类别坐标轴标签间距,以及定位饼图标签。
设置图表数据标签的数据精度
使用 NumberFormatOfValues 来格式化系列值。此示例创建了一个带有默认数据的折线图,显示其数据表,并为第一个系列启用数值标签。格式 #,##0.00 显示千位分隔符和两位小数,而不更改底层数值。
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.Line, 50, 50, 450, 300);
chart.HasDataTable = true;
var series = chart.ChartData.Series[0];
series.NumberFormatOfValues = "#,##0.00";
series.Labels.DefaultDataLabelFormat.ShowValue = true;
presentation.Save("PrecisionOfDatalabels_out.pptx", SaveFormat.Pptx);
要格式化图表嵌入工作簿中的数值单元格,请参阅 Set Custom Number Formats for Workbook Cells。该示例还展示了数值标签遵循其源单元格格式,同时保留存储的数值。
以标签显示百分比
对于堆积柱形图,计算每个值相对于其类别总计的百分比,并将文本分配给 TextFrameForOverriding。此示例使用默认图表数据,并以 8 磅字体显示两位小数的百分比。总计为零的类别将被跳过,以避免除以零。如果图表数据更改,需要重新计算自定义标签文本。
using System;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.StackedColumn, 20, 20, 400, 400);
var categoryTotals = new double[chart.ChartData.Categories.Count];
for (int k = 0; k < chart.ChartData.Categories.Count; k++)
{
for (int i = 0; i < chart.ChartData.Series.Count; i++)
{
var series = chart.ChartData.Series[i];
var pointValue = Convert.ToDouble(series.DataPoints[k].Value.Data);
categoryTotals[k] += pointValue;
}
}
for (int x = 0; x < chart.ChartData.Series.Count; x++)
{
var series = chart.ChartData.Series[x];
series.Labels.DefaultDataLabelFormat.ShowLegendKey = false;
for (int j = 0; j < series.DataPoints.Count; j++)
{
var label = series.DataPoints[j].Label;
if (categoryTotals[j] == 0)
{
continue;
}
var pointValue = Convert.ToDouble(series.DataPoints[j].Value.Data);
var dataPointPercent = (pointValue / categoryTotals[j]) * 100;
var portion = new Portion();
portion.Text = string.Format("{0:F2} %", dataPointPercent);
portion.PortionFormat.FontHeight = 8f;
label.TextFrameForOverriding.Text = "";
var paragraph = label.TextFrameForOverriding.Paragraphs[0];
paragraph.Portions.Add(portion);
label.DataLabelFormat.ShowValue = true;
label.DataLabelFormat.ShowSeriesName = false;
label.DataLabelFormat.ShowPercentage = false;
label.DataLabelFormat.ShowLegendKey = false;
label.DataLabelFormat.ShowCategoryName = false;
label.DataLabelFormat.ShowBubbleSize = false;
}
}
presentation.Save("DisplayPercentageAsLabels_out.pptx", SaveFormat.Pptx);
在图表数据标签中设置百分号
当数值以分数形式存储时,使用 NumberFormat 来显示百分比。将 IsNumberFormatLinkedToSource 设置为 false,以使标签格式独立于源单元格。
此示例创建了一个 100% 堆积柱形图,其中红色和蓝色系列跨四个类别。每对数值相加为 1。标签格式 0.0% 将 0.30 显示为 30.0%,而垂直坐标轴使用两位小数。两个系列均使用白色、10 磅的标签文本。
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.PercentsStackedColumn, 20, 20, 500, 400);
chart.Axes.VerticalAxis.IsNumberFormatLinkedToSource = false;
chart.Axes.VerticalAxis.NumberFormat = "0.00%";
chart.ChartData.Series.Clear();
chart.ChartData.Categories.Clear();
var workbook = chart.ChartData.ChartDataWorkbook;
int worksheetIndex = 0;
for (int i = 0; i < 4; i++)
{
var categoryCell = workbook.GetCell(worksheetIndex, i + 1, 0, $"Category {i + 1}");
chart.ChartData.Categories.Add(categoryCell);
}
string[] seriesNames = { "Reds", "Blues" };
Color[] seriesColors = { Color.Red, Color.Blue };
double[,] values = { { 0.30, 0.50, 0.80, 0.65 }, { 0.70, 0.50, 0.20, 0.35 } };
for (int i = 0; i < seriesNames.Length; i++)
{
var seriesCell = workbook.GetCell(worksheetIndex, 0, i + 1, seriesNames[i]);
var series = chart.ChartData.Series.Add(seriesCell, chart.Type);
for (int j = 0; j < 4; j++)
{
var valueCell = workbook.GetCell(worksheetIndex, j + 1, i + 1, values[i, j]);
series.DataPoints.AddDataPointForBarSeries(valueCell);
}
series.Format.Fill.FillType = FillType.Solid;
series.Format.Fill.SolidFillColor.Color = seriesColors[i];
var labelFormat = series.Labels.DefaultDataLabelFormat;
labelFormat.ShowValue = true;
labelFormat.IsNumberFormatLinkedToSource = false;
labelFormat.NumberFormat = "0.0%";
labelFormat.TextFormat.PortionFormat.FontHeight = 10;
labelFormat.TextFormat.PortionFormat.FillFormat.FillType = FillType.Solid;
labelFormat.TextFormat.PortionFormat.FillFormat.SolidFillColor.Color = Color.White;
}
presentation.Save("SetDataLabelsPercentageSign_out.pptx", SaveFormat.Pptx);
读取数据标签的实际文本
使用 GetActualLabelText 来检索数据标签设置生成的文本。这在提取报告标签、搜索演示文稿内容或验证生成的图表时非常有用。在下面的示例中,默认的 data label format 将每个类别名称、系列名称和数值组合在一起。一个数据点将其数值格式化为百分比,另一个使用来自 TextFrameForOverriding 的自定义文本。
using System;
using Aspose.Slides;
using Aspose.Slides.Charts;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 300);
chart.ChartData.Series.Clear();
chart.ChartData.Categories.Clear();
var workbook = chart.ChartData.ChartDataWorkbook;
chart.ChartData.Categories.Add(workbook.GetCell(0, 1, 0, "Q1"));
chart.ChartData.Categories.Add(workbook.GetCell(0, 2, 0, "Q2"));
var north = chart.ChartData.Series.Add(workbook.GetCell(0, 0, 1, "North"), chart.Type);
north.DataPoints.AddDataPointForBarSeries(workbook.GetCell(0, 1, 1, 0.25));
north.DataPoints.AddDataPointForBarSeries(workbook.GetCell(0, 2, 1, 0.75));
var south = chart.ChartData.Series.Add(workbook.GetCell(0, 0, 2, "South"), chart.Type);
south.DataPoints.AddDataPointForBarSeries(workbook.GetCell(0, 1, 2, 0.40));
south.DataPoints.AddDataPointForBarSeries(workbook.GetCell(0, 2, 2, 0.60));
foreach (var series in chart.ChartData.Series)
{
var format = series.Labels.DefaultDataLabelFormat;
format.ShowCategoryName = true;
format.ShowSeriesName = true;
format.ShowValue = true;
}
north.Labels[1].DataLabelFormat.IsNumberFormatLinkedToSource = false;
north.Labels[1].DataLabelFormat.NumberFormat = "0%";
south.Labels[0].TextFrameForOverriding.Text = "Reviewed";
foreach (var series in chart.ChartData.Series)
{
foreach (var point in series.DataPoints)
{
var label = point.Label;
if (!label.IsVisible)
{
continue;
}
Console.WriteLine($"Value: {point.Value.Data}; label: {label.GetActualLabelText()}");
}
}
数据点中存储的数值仍为 0.75,即使其标签显示为 75% 并附带类别和系列名称。自定义文本会替代生成的标签文本。GetActualLabelText 在两种情况下均返回相应的标签字符串。如上所示,当只想提取可见标签时,需要单独检查 IsVisible。
控制超出坐标轴最大值的数据标签
当手动限制坐标轴范围时,某些数据点可能超出其最大值。使用 ShowDataLabelsOverMaximum 来控制是否显示这些数据标签。此设置仅改变标签可见性,不会更改坐标轴范围或底层数据值。
下面的示例创建了一个值为 60 和 120 的 2D 集群柱形图。它在垂直坐标轴上将 IsAutomaticMaxValue 设置为 false,并将 MaxValue 设置为 100。第一张幻灯片允许超出最大值的标签;该幻灯片的副本禁用这些标签。两张幻灯片均保存为 DataLabelsOverMaximum.pptx。
使用 ShowValue 启用数值标签。图表级别的设置本身不会启用数值显示,也不会覆盖单个标签的禁用数值显示。此示例为整个系列启用数值,并使用 Position 将标签放置在每根柱形的外侧端点。
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 50, 50, 600, 400);
chart.HasLegend = false;
chart.ChartData.Series.Clear();
chart.ChartData.Categories.Clear();
var workbook = chart.ChartData.ChartDataWorkbook;
var firstCategory = workbook.GetCell(0, 1, 0, "Within range");
var secondCategory = workbook.GetCell(0, 2, 0, "Above maximum");
chart.ChartData.Categories.Add(firstCategory);
chart.ChartData.Categories.Add(secondCategory);
var seriesName = workbook.GetCell(0, 0, 1, "Values");
var series = chart.ChartData.Series.Add(seriesName, chart.Type);
var firstValue = workbook.GetCell(0, 1, 1, 60);
var secondValue = workbook.GetCell(0, 2, 1, 120);
series.DataPoints.AddDataPointForBarSeries(firstValue);
series.DataPoints.AddDataPointForBarSeries(secondValue);
series.Labels.DefaultDataLabelFormat.ShowValue = true;
series.Labels.DefaultDataLabelFormat.Position = LegendDataLabelPosition.OutsideEnd;
chart.Axes.VerticalAxis.IsAutomaticMaxValue = false;
chart.Axes.VerticalAxis.MaxValue = 100;
chart.ShowDataLabelsOverMaximum = true;
var secondSlide = presentation.Slides.AddClone(slide);
var secondChart = (IChart)secondSlide.Shapes[0];
secondChart.ShowDataLabelsOverMaximum = false;
presentation.Save("DataLabelsOverMaximum.pptx", SaveFormat.Pptx);
以下图像显示了 Microsoft PowerPoint 渲染的已保存幻灯片。true 时,标签 120 显示在上边界;false 时,该标签被隐藏。标签 60 保持可见,坐标轴最大值保持在 100,第二个数据点在两种情况下均为 120。
| ShowDataLabelsOverMaximum = true | ShowDataLabelsOverMaximum = false |
|---|---|
![]() |
![]() |
Chart Type
此示例使用了带数值坐标轴的 2D 柱形图。没有数值坐标轴的图表,如饼图和环形图,无法以这种方式限制坐标轴最大值。设置标签与坐标轴的距离
使用 LabelOffset 来控制类别坐标轴标签与坐标轴之间的距离。该数值是坐标轴标签最大字体大小的百分比。此示例创建了一个集群柱形图,并将水平坐标轴标签偏移设置为 500。此设置影响类别坐标轴标签,而不是附加到各个数据点的标签。
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 300);
chart.Axes.HorizontalAxis.LabelOffset = 500;
presentation.Save("SetCategoryAxisLabelDistance_out.pptx", SaveFormat.Pptx);
调整标签位置
在饼图上,调整数据标签位置以改善间距并为引导线留出空间。
此示例显示第一个数据点的数值,将其标签放置在切片外部,并调整其 X 和 Y 偏移。这些偏移分别相对于图表的宽度和高度。
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.Pie, 50, 50, 200, 200);
var series = chart.ChartData.Series;
var label = series[0].Labels[0];
label.DataLabelFormat.ShowValue = true;
label.DataLabelFormat.Position = LegendDataLabelPosition.OutsideEnd;
label.X = 0.71f;
label.Y = 0.04f;
presentation.Save("presentation.pptx", SaveFormat.Pptx);

格式化引导线
当饼图标签位于切片外部时,引导线帮助读者将每个数值与相应切片对应。对比色和略微加粗的线条可以使这些连接更易于辨认,而透明度和虚线样式则保持线条的视觉不突兀。
使用 LeaderLinesFormat 在系列的数据标签集合层级格式化引导线。series.Labels.LeaderLinesFormat.Line 中的设置适用于整个集合,而不是单个标签。
此示例创建了一个默认数据的饼图,在切片外部显示数值,并启用 ShowLeaderLines。它使用 X 和 Y 偏移,将两个标签进一步远离其切片,偏移以图表宽度和高度的比例表示。无需输入演示文稿。
该线条的 FillFormat 设置为实心蓝色,透明度值为 191(在 255 中),约为 25% 透明度。Width 将粗细设为 2 磅,且 DashStyle 将线条设为虚线。
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.Pie, 80, 50, 560, 400);
chart.HasLegend = false;
var series = chart.ChartData.Series[0];
var labelFormat = series.Labels.DefaultDataLabelFormat;
labelFormat.ShowValue = true;
labelFormat.Position = LegendDataLabelPosition.OutsideEnd;
labelFormat.ShowLeaderLines = true;
// 将两个标签从它们的切片中向外移动得更远。
series.Labels[0].X = 0.10f;
series.Labels[0].Y = -0.05f;
series.Labels[1].X = -0.10f;
series.Labels[1].Y = 0.05f;
var line = series.Labels.LeaderLinesFormat.Line;
line.FillFormat.FillType = FillType.Solid;
line.FillFormat.SolidFillColor.Color = Color.FromArgb(191, 30, 90, 160);
line.Width = 2;
line.DashStyle = LineDashStyle.Dash;
presentation.Save("FormattedLeaderLines.pptx", SaveFormat.Pptx);
启用引导线并不保证每个标签都会出现线条。可见性取决于标签位置和图表的布局行为。在此示例中,两个移位的标签具有可见的引导线;靠近切片的标签则不需要引导线。
以下图像显示了使用 Aspose.Slides 渲染的已保存 FormattedLeaderLines.pptx:

在柱形图上方添加多行数据标签
此示例创建了一个柱形图,在绘图区上方添加了两行数据标签。Series A 显示可见的柱形,而 Series B 和 Series C 提供额外的标签。通过移除填充和轮廓来隐藏它们的柱形。Overlap 属性使所有三个系列在相同的类别中心对齐。
PlotArea 设置为标签行预留空间。调用 ValidateChartLayout 计算默认位置后,使用 X 和 Y 保持水平对齐并应用垂直偏移,将标签排列成两行。数值仍然是链接到系列数值的数据标签;只有行标题是独立的文本形状。
using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;
using var presentation = new Presentation();
var slide = presentation.Slides[0];
var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 40, 40, 640, 200);
chart.HasTitle = false;
chart.HasLegend = false;
chart.TextFormat.PortionFormat.FontHeight = 12;
chart.ChartData.Series.Clear();
chart.ChartData.Categories.Clear();
var workbook = chart.ChartData.ChartDataWorkbook;
workbook.Clear(0);
string[] categories = { "North", "South", "East", "West" };
string[] seriesNames = { "Series A", "Series B", "Series C" };
double[,] seriesValues = {
{ 35, 42, 28, 47 },
{ 22, 31, 19, 26 },
{ 12, 16, 14, 18 }
};
for (int categoryIndex = 0; categoryIndex < categories.Length; categoryIndex++)
{
var categoryCell = workbook.GetCell(0, categoryIndex + 1, 0, categories[categoryIndex]);
chart.ChartData.Categories.Add(categoryCell);
}
for (int seriesIndex = 0; seriesIndex < seriesNames.Length; seriesIndex++)
{
var seriesCell = workbook.GetCell(0, 0, seriesIndex + 1, seriesNames[seriesIndex]);
var series = chart.ChartData.Series.Add(seriesCell, ChartType.ClusteredColumn);
for (int categoryIndex = 0; categoryIndex < categories.Length; categoryIndex++)
{
var valueCell = workbook.GetCell(0, categoryIndex + 1, seriesIndex + 1, seriesValues[seriesIndex, categoryIndex]);
series.DataPoints.AddDataPointForBarSeries(valueCell);
}
if (seriesIndex > 0)
{
// 隐藏 B 和 C 的柱形,但保留它们的数据标签。
series.Format.Fill.FillType = FillType.NoFill;
series.Format.Line.FillFormat.FillType = FillType.NoFill;
var labelFormat = series.Labels.DefaultDataLabelFormat;
labelFormat.ShowValue = true;
labelFormat.TextFormat.PortionFormat.FontHeight = 12;
var labelFill = labelFormat.TextFormat.PortionFormat.FillFormat;
labelFill.FillType = FillType.Solid;
labelFill.SolidFillColor.Color = Color.Black;
labelFormat.Position = LegendDataLabelPosition.InsideBase;
}
}
// 使所有三个系列在相同的类别中心对齐。
chart.ChartData.Series[0].ParentSeriesGroup.Overlap = 100;
// 在此紧凑示例中使用更少的网格线。
chart.Axes.VerticalAxis.IsAutomaticMajorUnit = false;
chart.Axes.VerticalAxis.MajorUnit = 10;
// 在绘图区上方预留空间,以容纳两行数据标签。
chart.PlotArea.LayoutTargetType = LayoutTargetType.Inner;
chart.PlotArea.X = 0.15f;
chart.PlotArea.Y = 0.32f;
chart.PlotArea.Width = 0.80f;
chart.PlotArea.Height = 0.48f;
chart.ValidateChartLayout();
for (int seriesIndex = 1; seriesIndex < seriesNames.Length; seriesIndex++)
{
var series = chart.ChartData.Series[seriesIndex];
float rowTop = seriesIndex == 1 ? 0.15f : 0.03f;
for (int categoryIndex = 0; categoryIndex < categories.Length; categoryIndex++)
{
var dataLabel = series.DataPoints[categoryIndex].Label;
// 保持默认的水平位置。Y 是相对于
// 默认标签位置的偏移量,以图表高度的比例表示。
dataLabel.X = 0;
dataLabel.Y = rowTop - dataLabel.ActualY / chart.Height;
}
// 仅行标题是单独的文本形状。
var rowHeading = slide.Shapes.AddAutoShape(ShapeType.Rectangle, chart.X, chart.Y + rowTop * chart.Height, 85, 18);
rowHeading.FillFormat.FillType = FillType.NoFill;
rowHeading.LineFormat.FillFormat.FillType = FillType.NoFill;
rowHeading.AddTextFrame(seriesNames[seriesIndex]);
rowHeading.TextFrame.TextFrameFormat.MarginTop = 0;
rowHeading.TextFrame.TextFrameFormat.MarginBottom = 0;
var headingFormat = rowHeading.TextFrame.Paragraphs[0].Portions[0].PortionFormat;
headingFormat.FontHeight = 12;
headingFormat.FillFormat.FillType = FillType.Solid;
headingFormat.FillFormat.SolidFillColor.Color = Color.Black;
}
presentation.Save("multiple-rows-of-labels.pptx", SaveFormat.Pptx);

常见问题
如何防止在密集图表上数据标签重叠?
结合自动标签布局、引导线和缩小字体大小;必要时,隐藏某些字段(例如类别),或仅对极值或关键点显示标签。
如何仅对零、负数或空值禁用标签?
在启用标签之前过滤数据点,并依据定义的规则关闭对值为 0、负数或缺失值的显示。
在导出为 PDF/图片时,如何确保标签样式一致?
显式设置字体族和大小,并确认渲染环境中存在该字体,以避免回退。

