在 .NET 中管理演示文稿的图表数据系列

概述

图表将绘制的数据存储在图表数据工作簿中。
IChartSeries 表示一组相关值,系列中的每个 IChartDataPoint 对应一个或多个工作簿单元格。
IChartCategory 对象提供系列共享的标签或分组值。
因此,系列名称、类别和数据点值都连接到 IChartDataCell 对象,而不是仅作为显示文本存储。

对于典型的类别图表,默认工作簿使用第 0 行存放系列名称,第 0 列存放类别名称,其余单元格存放系列数值。传递给 IChartDataWorkbook.GetCell 的工作表、行和列索引采用从零开始的编号。此布局在创建具有默认数据的图表时很有用,但不要假设每个现有图表都使用该布局。对于已加载的演示文稿,请在更改工作簿数值之前检查系列、类别和数据点引用的单元格。

图表设置有三种不同的作用范围:

当未设置明确的点或系列填充时,图表样式和主题决定自动外观。当系列和数据点的格式同时存在时,数据点的格式优先适用于该点。

图表系列示例

设置图表系列重叠

IChartSeries.Overlap 报告 2D 图表中柱形或条形的重叠程度,范围为 -100% 到 100%。它是父系列组设置的只读投影。设置 IChartSeriesGroup.Overlap 可更新该组中所有兼容的系列。此选项适用于显示分组柱形或条形的图表类型;对组合图表中不相关的系列组没有影响。

以下示例为包含第一个系列的组设置重叠:

using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int firstSeriesIndex = 0;
const sbyte overlapPercent = 30;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

// 新图表包含示例系列、类别和数值。
var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

var series = chart.ChartData.Series[firstSeriesIndex];
series.ParentSeriesGroup.Overlap = overlapPercent;

presentation.Save("series_overlap.pptx", SaveFormat.Pptx);

结果:

系列重叠

更改系列填充颜色

使用 IChartSeries.Format 为整个系列设置默认填充。如果某个数据点已经拥有显式填充,其 IChartDataPoint.Format 设置会覆盖该系列的填充。

以下示例为第一个系列应用实心蓝色填充:

using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int firstSeriesIndex = 0;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

var series = chart.ChartData.Series[firstSeriesIndex];
series.Format.Fill.FillType = FillType.Solid;
series.Format.Fill.SolidFillColor.Color = Color.Blue;

presentation.Save("series_color.pptx", SaveFormat.Pptx);

结果:

系列颜色

更改系列名称

系列名称存储在图表数据工作簿中,通常显示在图例中。在为聚集柱形图创建的默认工作簿中,单元格 B1 位于第 0 行第 1 列,包含第一个系列的名称。以下示例中的命名常量明确了该结构:

using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int worksheetIndex = 0;
const int seriesNameRowIndex = 0;
const int firstSeriesColumnIndex = 1;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

var workbook = chart.ChartData.ChartDataWorkbook;
var seriesNameCell = workbook.GetCell(worksheetIndex, seriesNameRowIndex, firstSeriesColumnIndex);
seriesNameCell.Value = "Revenue";

presentation.Save("series_name.pptx", SaveFormat.Pptx);

您也可以更新 IChartSeries.Name 已引用的单元格。这种方法避免了对现有图表中具体行列的假设:

using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int firstSeriesIndex = 0;
const int firstNameCellIndex = 0;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

var series = chart.ChartData.Series[firstSeriesIndex];
var seriesNameCell = series.Name.AsCells[firstNameCellIndex];
seriesNameCell.Value = "Revenue";

presentation.Save("series_name.pptx", SaveFormat.Pptx);

结果:

系列名称

获取自动系列填充颜色

IChartSeries.GetAutomaticSeriesColor 返回根据系列索引和图表样式计算得到的颜色。当系列填充未明确定义时,将使用此颜色。调用该方法仅获取计算后的颜色,不会分配新的填充。

以下示例输出每个默认系列的自动颜色:

using System;
using Aspose.Slides;
using Aspose.Slides.Charts;

const int firstSlideIndex = 0;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

var seriesCount = chart.ChartData.Series.Count;
for (var seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++)
{
    var series = chart.ChartData.Series[seriesIndex];
    var automaticColor = series.GetAutomaticSeriesColor();
    Console.WriteLine($"Series {seriesIndex}: {automaticColor.Name}");
}

默认图表样式的示例输出:

Series 0: ff4f81bd
Series 1: ffc0504d
Series 2: ff9bbb59

确切的颜色取决于图表样式和主题。

为图表系列设置负值反转填充颜色

对于条形、柱形和气泡系列,IChartSeries.InvertIfNegative 可以使用不同的填充来显示负值。将常规系列填充设为实心,启用反转,并通过 IChartSeries.InvertedSolidFillColor 指定负值颜色。负数在工作簿中保持不变,仅改变其显示颜色。

以下示例用单一系列替换默认图表数据。工作表第 0 行包含系列名称,第 0 列包含类别名称,第 1 列包含数值:

using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int worksheetIndex = 0;
const int headerRowIndex = 0;
const int categoryColumnIndex = 0;
const int firstSeriesColumnIndex = 1;
const int firstDataRowIndex = 1;

var categoryNames = new[] { "Category 1", "Category 2", "Category 3" };
var seriesValues = new[] { -20, 50, -30 };

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);
var chartData = chart.ChartData;
var workbook = chartData.ChartDataWorkbook;

chartData.Series.Clear();
chartData.Categories.Clear();

var seriesNameCell = workbook.GetCell(worksheetIndex, headerRowIndex, firstSeriesColumnIndex, "Series 1");
var series = chartData.Series.Add(seriesNameCell, chart.Type);

for (var categoryIndex = 0; categoryIndex < categoryNames.Length; categoryIndex++)
{
    var dataRowIndex = firstDataRowIndex + categoryIndex;
    var categoryName = categoryNames[categoryIndex];
    var seriesValue = seriesValues[categoryIndex];

    var categoryCell = workbook.GetCell(worksheetIndex, dataRowIndex, categoryColumnIndex, categoryName);
    chartData.Categories.Add(categoryCell);

    var valueCell = workbook.GetCell(worksheetIndex, dataRowIndex, firstSeriesColumnIndex, seriesValue);
    series.DataPoints.AddDataPointForBarSeries(valueCell);
}

var automaticSeriesColor = series.GetAutomaticSeriesColor();
series.Format.Fill.FillType = FillType.Solid;
series.Format.Fill.SolidFillColor.Color = automaticSeriesColor;
series.InvertIfNegative = true;
series.InvertedSolidFillColor.Color = Color.Red;

presentation.Save("inverted_solid_fill_color.pptx", SaveFormat.Pptx);

结果:

反转实心填充颜色

您可以通过 IChartDataPoint.InvertIfNegative 为单个数据点启用反转。在下例中,系列的反转被禁用,仅为选中的数据点启用反转,并为该点分配负值以显示效果:

using System.Drawing;
using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int firstSeriesIndex = 0;
const int targetDataPointIndex = 2;
const int negativeValue = -30;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

var series = chart.ChartData.Series[firstSeriesIndex];
var automaticSeriesColor = series.GetAutomaticSeriesColor();
series.Format.Fill.FillType = FillType.Solid;
series.Format.Fill.SolidFillColor.Color = automaticSeriesColor;
series.InvertedSolidFillColor.Color = Color.Red;
series.InvertIfNegative = false;

var dataPoint = series.DataPoints[targetDataPointIndex];
dataPoint.YValue.AsCell.Value = negativeValue;
dataPoint.InvertIfNegative = true;

presentation.Save("data_point_invert_color_if_negative.pptx", SaveFormat.Pptx);

清除特定数据点的值

要使单个数据点为空而不删除其他数据点,请将其对应的工作簿单元格设为 null。对于柱形图,可通过 IChartDataPoint.YValue 获取绘制值。数据点仍保持在相同的类别位置,但图表会根据空值设置将其视为空白。

以下示例仅清除第一系列的第二个数据点:

using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int firstSeriesIndex = 0;
const int targetDataPointIndex = 1;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

var series = chart.ChartData.Series[firstSeriesIndex];
var dataPoint = series.DataPoints[targetDataPointIndex];
dataPoint.YValue.AsCell.Value = null;

presentation.Save("clear_data_point_value.pptx", SaveFormat.Pptx);

散点图使用单独的 X 和 Y 单元格,气泡图还使用大小单元格。仅清除表示您想移除的数值的单元格。若想保留其他数据点,请勿调用 IChartDataPointCollection.Clear,因为该方法会删除该系列中的所有数据点。

控制空单元格的显示

包含数值的隐藏单元格与空单元格是不同的情况。要在隐藏的工作表行和列中包含或排除数据,请参阅 包含隐藏行和列的数据。

空工作簿单元格表示缺失数据;包含 0 的单元格表示已知的数值。将 IChartDataCell.Value 设为 null 可使单元格为空。无论空单元格设置如何,数值零仍保持为零。

使用 IChart.DisplayBlanksAs 选择图表显示空单元格的方式。此设置适用于整个图表。它改变空白的绘制方式,但不会用零或插值填充空工作簿单元格。

下面的独立示例创建一个包含单个系列的折线图,清除第 3 天的数值,并分别以每种模式保存同一图表。无需输入文件。IChartDataWorkbook 使用工作表 0,列 0 作为类别标签,列 1 作为数值;第 0 行保存系列名称。最终数据为 10, 20, empty, 30, 40。

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.LineWithMarkers, 40, 40, 640, 400);
var chartData = chart.ChartData;
var workbook = chartData.ChartDataWorkbook;

chartData.Series.Clear();
chartData.Categories.Clear();

var seriesNameCell = workbook.GetCell(0, 0, 1, "Measurements");
var series = chartData.Series.Add(seriesNameCell, chart.Type);
var values = new[] { 10, 20, 25, 30, 40 };

for (var i = 0; i < values.Length; i++)
{
    var categoryCell = workbook.GetCell(0, i + 1, 0, $"Day {i + 1}");
    chartData.Categories.Add(categoryCell);
    var valueCell = workbook.GetCell(0, i + 1, 1, values[i]);
    series.DataPoints.AddDataPointForLineSeries(valueCell);
}

// Leave Day 3 genuinely empty, while retaining its category and data point.
workbook.GetCell(0, 3, 1).Value = null;

var modes = new[] { DisplayBlanksAsType.Gap, DisplayBlanksAsType.Zero, DisplayBlanksAsType.Span };
foreach (var mode in modes)
{
    chart.DisplayBlanksAs = mode;
    presentation.Save($"empty_cells_{mode}.pptx", SaveFormat.Pptx);
}

每个输出文件在保存前存储所分配的模式:empty_cells_Gap.pptx、empty_cells_Zero.pptx 和 empty_cells_Span.pptx。若仅需保存一种版本,请在保存演示文稿前设置所需模式并一次性保存,而不是遍历所有模式。

下面的比较显示三个文件中相同的数据。第 3 天在工作簿中均为空:

折线图示例:Gap 在第 3 天断开线段,Zero 将线段降至零,Span 将第 2 天与第 4 天连接。

可见效果取决于图表类型。折线图能够轻松比较全部三种模式。柱形和条形图没有跨缺失类别的连线,因此 Span 无法生成上述的连接段;缺失的柱形和零高度的柱形看起来也相似。同样,仅带标记的散点图没有连线。不要期望每种图表类型都有三种不同的结果;请检查所使用类型的输出。

设置系列间隙宽度

间隙宽度是相邻柱形或条形簇之间的空间,表示为柱形或条形宽度的百分比。与重叠类似,它属于父系列组,而非单个系列。为组一次性设置 IChartSeriesGroup.GapWidth。较大的数值会在簇之间创建更多空间,较小的数值则使其更密集。

以下示例更改间隙宽度并仅保存最终演示文稿:

using Aspose.Slides;
using Aspose.Slides.Charts;
using Aspose.Slides.Export;

const int firstSlideIndex = 0;
const int firstSeriesIndex = 0;
const int gapWidthPercent = 30;

using var presentation = new Presentation();
var slide = presentation.Slides[firstSlideIndex];

var chart = slide.Shapes.AddChart(ChartType.StackedColumn, 20, 20, 500, 200);

var series = chart.ChartData.Series[firstSeriesIndex];
series.ParentSeriesGroup.GapWidth = gapWidthPercent;

presentation.Save("gap_width_30.pptx", SaveFormat.Pptx);

结果:

间隙宽度

常见问题

哪些图表类型支持数据系列?
[ChartType] 枚举表示的所有图表类型都使用图表数据,但它们的系列并不都有相同的数值结构或设置。例如,类别图使用类别和数值,散点图使用 X 与 Y 数值,气泡图还包括气泡尺寸。请使用与系列类型相匹配的数据点创建方法。重叠和间隙宽度等选项仅适用于兼容的条形或柱形组。

什么是图表系列组?
[IChartSeriesGroup] 包含共享组级绘图设置的兼容系列。组合图表可以包含多个组,因此通过某个系列访问的组的更改不一定会影响图表中的所有系列。

新创建的图表是否包含默认数据?
是的。默认情况下,[IShapeCollection.AddChart] 会创建示例系列、类别和数值。您可以编辑这些单元格或在添加完全自定义的数据集之前清除系列和类别集合。还有重载可在不包含默认数据的情况下创建图表。

图表对象如何与工作簿单元格关联?
系列名称、类别标签和数据点数值引用 IChartDataWorkbook 中的单元格。更改被引用的单元格会更新相应的图表元素。构建自定义数据时,请保持类别行和系列数值行对齐,以便每个数据点绘制在预期的类别下。

如何只清除一个点而不是整个系列?
将相关数值单元格设为 null,即可保留该点的类别位置为一个空点。仅在需要删除该系列全部数据点时才使用 [IChartDataPointCollection.Clear]。如果同时删除了类别,请更新所有系列,使其数值仍与类别集合保持对齐。

空数据点如何显示?
结果取决于图表类型和 [IChart.DisplayBlanksAs]。受支持的图表可以将空白显示为间隙、零值或通过连接相邻点来显示。请选择与演示文稿中缺失数据含义相匹配的设置。完整示例和可视化比较请参阅 控制空单元格的显示。

负值如何格式化?
对于受支持的条形、柱形和气泡系列,启用 [IChartSeries.InvertIfNegative] 并设置 [IChartSeries.InvertedSolidFillColor]。您可以通过 [IChartDataPoint.InvertIfNegative] 为单个数据点覆盖此行为。这些属性影响格式,而不改变存储的数值。

当系列和数据点都被格式化时,哪种格式优先?
对该点而言,显式的数据点格式优先。其他点继续使用显式的系列格式,若系列格式未定义,则使用自动的图表样式和主题。组属性(如重叠和间隙宽度)控制布局,并非数据点级别的格式覆盖。

图表可以包含的系列数量有限制吗?
Aspose.Slides 并未设置单独的系列数量上限。实际使用中,演示文稿文件的限制、可用内存、渲染时间以及图表可读性决定了实际可用的上限。

当柱形过于紧密或过于稀疏时应该如何调整?
在相应的父系列组上设置 [IChartSeriesGroup.GapWidth]。增大数值可扩大簇之间的间距,减小数值则使簇更靠近。