# 这是什么

npoi 是开源的 C# 处理 Word、Excel 的库,无需使用数据库或 COM 组件,原理为 xml 处理

# 问题

npoi 自带的 Excel 的折线图绘制中有以下问题:

  • 无法设置曲线是否平滑
  • 无法标记数据点

# NPOI 简易使用

# Excel

# 数据写入

  1. 首先创建一个 Sheet
1
2
IWorkbook wb = new XSSFWorkbook();
ISheet sheet = wb.CreateSheet("Sheet1");
  1. 之后按照坐标的方式写入数据,此处展示一行一行写入的方法
1
2
3
4
5
IRow row = sheet.CreateRow(0);
row.CreateCell(0).SetCellValue("a");
row.CreateCell(1).SetCellValue(1);
row.CreateCell(2).SetCellValue("1");
row.CreateCell(3).SetCellValue(DateTime.Now.ToString("G"));

file
由上图可见,单元格内容格式随填入 Value 的类型变化而变化,注意数字与文本的类型区别

  1. 保存表格
1
2
3
4
using (FileStream fs = File.Create("test.xlsx"))
{
wb.Write(fs);
}

# 绘制折线图

  1. 创建图表对象
1
2
IDrawing drawing = sheet.CreateDrawingPatriarch();
IClientAnchor anchor = drawing.CreateAnchor(0, 0, 0, 0, 0, 5, 10, 15);

anchor 为图表所在的位置以及图表的大小, CreateAnchor 中控制图表位置大小的参数在后 4 个参数,分别为左上角的坐标以及右下角的坐标,且左上角在设置的 row 之下但右上角在设置的 row 之上
file

  1. 设置图表数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
XSSFChart chart = (XSSFChart)drawing.CreateChart(anchor);
chart.SetTitle("Test 1");
IChartLegend legend = chart.GetOrCreateLegend();
legend.Position = LegendPosition.TopRight;
ILineChartData<double, double> data = chart.ChartDataFactory.CreateLineChartData<double, double>();
IChartAxis bottomAxis = chart.ChartAxisFactory.CreateCategoryAxis(AxisPosition.Bottom);
IValueAxis leftAxis = chart.ChartAxisFactory.CreateValueAxis(AxisPosition.Left);
leftAxis.Crosses = AxisCrosses.AutoZero;
IChartDataSource<double> xs = DataSources.FromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
IChartDataSource<double> ys1 = DataSources.FromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
IChartDataSource<double> ys2 = DataSources.FromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1));
var s1 = data.AddSeries(xs, ys1);
s1.SetTitle("Series 1");
var s2 = data.AddSeries(xs, ys2);
s2.SetTitle("Series 2");
chart.Plot(data, bottomAxis, leftAxis);

上述代码中,创建了两个系列,且将表格的第一行作为 X 轴,系列 1 将第二行作为 Y 值,系列 2 将第三行作为 Y 值,一一对应映射出整个图表。结果如上图所示

  1. 手动添加表格点
1
2
3
4
5
6
double[] arr = new double[] { 1, 2, 3, 4 };
double[] arr2 = new double[] { 8, 7, 6, 5 };
IChartDataSource<double> xs = DataSources.FromArray(arr);
IChartDataSource<double> ys = DataSources.FromArray(arr2);
var s1 = data.AddSeries(xs, ys);
s1.SetTitle("Series 1");

值也可使用 DateTime 类型

# 设置非平滑折线图

经过上述的代码,可创建出一个折线图,但是为平滑处理后的,不符合预想,且无法在数据点上显示具体数字。
查询过各种资料之后,了解到目前 NPOI 未提供修改这些参数的方法。但是偶然间发现了一篇使用 NPOI 绘制饼状图的文章,由于 NPOI 也未提供饼状图的绘制方法,所以就仔细查看了一下。
https://stackoverflow.com/a/66534351
其中调用了 CT_Chart.plotArea ,修改了更底层未暴露的参数来实现了效果,现版本的 NPOI 提供了方法暴露 CT_Chart ,不需要再使用反射的方法获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var plotArea = chart.GetCTChart().plotArea;
foreach (var item in plotArea.lineChart[0].ser)
{
item.AddNewMarker().AddNewSymbol().val = ST_MarkerStyle.diamond;// 数值点的标志形状
item.smooth = new CT_Boolean() { val = 0 };// 是否开启平滑
item.dLbls = new CT_DLbls
{
showVal = new CT_Boolean { val = 1 },// 点上显示数值
showSerName = new CT_Boolean { val = 0 },// 点上显示系列名称
showCatName = new CT_Boolean { val = 0 },// 点上显示X轴数值
showLegendKey = new CT_Boolean { val = 0 },// 点上显示系列标志
showLeaderLines = new CT_Boolean { val = 0 },// 点上显示系列标志时多显示一个横线
dLblPos = new CT_DLblPos { val = ST_DLblPos.t }// 点上显示数值的位置, t为顶部
};
}

file
非平滑、添加数据点
file
平滑