如何在C# EPPlus/OfficeOpenXML堆叠柱状图中添加文本框显示标签占比
需求与问题
我有一个堆叠柱状图,需要给每个标签(比如PDD1)添加文本框,把对应百分比显示在该标签下两组柱子的中间上方。这个百分比是标签的整体占比,不属于单个柱子。
我已经把百分比数据加到表格里了,但没法隐藏图表里的相关系列,也没法把百分比放到标签区域顶部的中间位置。查资料的时候发现OfficeOpenXML.Drawing.Chart里没有LoadEntries方法,只有只读的Entries属性。
下面是我的图表数据、C#代码、当前效果和期望效果,求用C#结合EPPlus或OfficeOpenXML实现需求的方案:
图表数据

C#代码
dynamic DataList5 = new { label = new List<string>() { "PDD1", "PDD2", "PDD3" }, modeler = new { Category1 = new List<int>() { 1, 1, 1 }, Category2 = new List<int>() { 0, 0, 0 } }, tester = new { Category1 = new List<int>() { 2, 2, 2 }, Category2 = new List<int>() { 2, 2, 2 } }, percentage = new List<int>() { 150, 200, 10 } }; if (DataList5.label.Count > 0) { int j = 0; for (int i = 0; i < DataList5.label.Count * 2; i++) { if (i % 2 == 0) { ws2.Cells[i + 2, 2].Value = DataList5.label[j]; ws2.Cells[i + 2, 3].Value = "Category 1"; ws2.Cells[i + 2, 4].Value = DataList5.modeler.Category1[j]; ws2.Cells[i + 2, 5].Value = DataList5.tester.Category1[j]; ws2.Cells[i + 2, 6].Value = DataList5.percentage[j]; } else { ws2.Cells[i + 2, 3].Value = "Category 2"; ws2.Cells[i + 2, 4].Value = DataList5.modeler.Category2[j]; ws2.Cells[i + 2, 5].Value = DataList5.tester.Category2[j]; j++; } } } else { ws2.Cells[2, 2].Value = "No Data Found"; } var chart1 = ws1.Drawings.AddChart("chart1", eChartType.ColumnStacked); chart1.SetPosition(1, 0, 0, 0); chart1.SetSize(1800, 400); chart1.Style = eChartStyle.Style12; if (DataList5.label.Count > 0) { var series1 = chart1.Series.Add(ws2.Cells["D2:D" + (DataList5.label.Count * 2 + 1)], ws2.Cells["B2:C" + (DataList5.label.Count * 2 + 1)]); var series2 = chart1.Series.Add(ws2.Cells["E2:E" + (DataList5.label.Count * 2 + 1)], ws2.Cells["B2:C" + (DataList5.label.Count * 2 + 1)]); //percentage series //var series3 = chart1.Series.Add(ws2.Cells["F2:F" + (DataList5.label.Count * 2 + 1)], ws2.Cells["B2:C" + (DataList5.label.Count * 2 + 1)]); series1.Fill.Color = Color.FromArgb(255, 0, 112, 192); series2.Fill.Color = Color.FromArgb(255, 239, 125, 49); //Show percentage for each PDD1 label ////chart1.Legend.LoadEntries(chart1.Legend.LegendEntries().Count).Delete(); series1.Header = "Modeler"; series2.Header = "Tester"; //series3.Header = "Percentage"; } else { var series1 = chart1.Series.Add(ws2.Cells["B2:C" + (DataList1.label.Count + 1)], ws2.Cells["B2:C" + (DataList1.label.Count + 1)]); } chart1.Title.Text = "Stacked bar chart example";
当前实现效果

期望效果

解决方案
思路
不用添加百分比系列,直接在图表上创建文本框,通过计算图表坐标定位到每个标签组的中间上方,读取表格中的百分比值填充即可,避开Legend相关的问题。
修改后的代码
在图表创建完成的逻辑块中,添加以下代码:
if (DataList5.label.Count > 0) { // 保留原有系列创建、样式设置代码... // 添加百分比文本框 var categoryAxis = chart1.Axes.CategoryAxis; var valueAxis = chart1.Axes.ValueAxis; for (int labelIndex = 0; labelIndex < DataList5.label.Count; labelIndex++) { // 计算当前标签组两个分类的X坐标,取中间值作为文本框X位置 int cat1Idx = labelIndex * 2; int cat2Idx = labelIndex * 2 + 1; var cat1Pos = categoryAxis.GetPosition(cat1Idx); var cat2Pos = categoryAxis.GetPosition(cat2Idx); double textX = (cat1Pos.Left + cat2Pos.Right) / 2 - 25; // 减去文本框半宽,确保居中 // 计算文本框Y位置:取当前标签组最高柱子的顶部,向上偏移20像素 int maxStack1 = DataList5.modeler.Category1[labelIndex] + DataList5.tester.Category1[labelIndex]; int maxStack2 = DataList5.modeler.Category2[labelIndex] + DataList5.tester.Category2[labelIndex]; double maxValue = Math.Max(maxStack1, maxStack2); double textY = valueAxis.GetPosition(maxValue).Top - 20; // 创建文本框 var textbox = chart1.Shapes.AddTextbox($"percent_{labelIndex}", textX, textY, 50, 20); textbox.Text = $"{DataList5.percentage[labelIndex]}%"; textbox.TextBody.Font.Size = 10; textbox.TextBody.Font.Bold = true; textbox.TextBody.Alignment = eTextAlignment.Center; textbox.Fill.SolidFill.Color = Color.White; textbox.Outline.Fill.SolidFill.Color = Color.Transparent; // 隐藏边框 } }
关键说明
- 坐标计算:通过
CategoryAxis.GetPosition获取分类的X坐标,取同一标签下两个分类的中间点;通过ValueAxis.GetPosition获取柱子最高值的Y坐标,向上偏移避免文本与柱子重叠。 - 文本框样式:设置居中对齐、白色填充和透明边框,避免遮挡图表内容。
- 无额外系列:直接用文本框展示百分比,无需添加隐藏系列,彻底避开Legend的操作问题。
注意事项
- 如果柱子高度变化,可调整
textY的偏移量,确保文本始终在柱子上方。 - 文本框宽度可根据百分比数值长度调整,避免内容截断。
内容的提问来源于stack exchange,提问作者rock
相关产品推荐
相关产品推荐

