我正在开发一个应用程序,我将在其中填写一些文本并想要更新一些图表。
到目前为止,我已经使用 OpenXML 成功完成了我想要的一切。但是我无法更新图表。图表的嵌入 Excel 已使用 OpenXML 进行更新,但图表本身并未更新。我想有一些缓存需要刷新。
有什么想法可以做到这一点吗?
private static void PopulateChart(WordprocessingDocument document, string contentControlName, List<List<decimal>> cellValues)
{
// Find drawing by alternative name
var drawing = document.MainDocumentPart?.Document.Body?.Descendants<Drawing>()
.FirstOrDefault(d => d.Inline?.DocProperties?.Description?.Value == contentControlName);
// Get the chart reference
var chartRef = drawing?.Descendants<ChartReference>().FirstOrDefault();
// Make sure the chart exists
if (chartRef == null || string.IsNullOrWhiteSpace(chartRef.Id))
{
// TODO ADD SERILOG
throw new ArgumentNullException($"Chart {contentControlName} not found or does not have a reference.");
}
var chartId = chartRef.Id.ToString()!;
var chartPart = (ChartPart) document.MainDocumentPart?.GetPartById(chartId)!;
var embeddedExcel = chartPart.EmbeddedPackagePart!;
if (embeddedExcel == null)
{
throw new ArgumentNullException($"Chart {contentControlName} embedded Excel resource not found.");
}
using var stream = embeddedExcel.GetStream();
using var excelDoc = SpreadsheetDocument.Open(stream, true);
// Get the worksheet part by name or index
var workbookPart = excelDoc.WorkbookPart!;
var worksheetPart = workbookPart.WorksheetParts.First();
// Load cell data.
var cells = worksheetPart.Worksheet
.Descendants<Cell>()
.ToArray();
// Start from row 2
var rowIndex = 2;
foreach (var row in cellValues)
{
// Column number
var columnLetterNumericalIndex = 0;
foreach (var column in row)
{
var cellReference = $"{GetColumnName(columnLetterNumericalIndex)}{rowIndex}";
cells.Single(c => c.CellReference == cellReference).CellValue = new CellValue(column);
columnLetterNumericalIndex++;
}
rowIndex++;
}
// Save the changes to the spreadsheet
chartPart.ChartSpace.Save();
worksheetPart.Worksheet.Save();
// Document is saved higher up (document.Save())
}
无法使用 Word Interop,因为该应用程序将托管在 Azure 中。
提前致谢。
您需要更新图表部分中的
<c:numCache>
元素。
我不知道你有什么类型的图表,也不知道它的格式选项(比如它是否有“切换行/列”选项),但尽管如此,这里有一个可以更新简单/默认条形图的示例:
// ...
int seriesIndex = 0;
foreach (var numCache in chartPart.ChartSpace.Descendants<NumberingCache>())
{
var points = numCache.Descendants<NumericPoint>().ToList();
for (int pointIndex = 0; pointIndex < points.Count; pointIndex++)
points[pointIndex].NumericValue.Text = cellValues[pointIndex][seriesIndex].ToString();
seriesIndex++;
}
// Save the changes to the spreadsheet
chartPart.ChartSpace.Save();
worksheetPart.Worksheet.Save();
// Document is saved higher up (document.Save())
除了 Mario Z 的出色解决方案之外,此方法将完成所有必要的繁重工作来更新给定图表的系列值。
/// <summary>
/// Update the series values for a given chart. We must update two components:
/// * Embedded worksheet series values
/// * Cached chart series values, which come from the embedded worksheet series values but don't refresh unless you edit them in PowerPoint itself
/// </summary>
/// <param name="chartPart">Chart part to update</param>
/// <param name="chartSeriesValues">Chart series values for the chart - we are only updating cell values in column B for rows >= 2</param>
public void UpdateChartSeriesValues(ChartPart chartPart, double[] chartSeriesValues)
{
EmbeddedPackagePart embeddedExcel = chartPart.EmbeddedPackagePart!;
if (embeddedExcel == null)
{
// Embedded Excel resource not found - don't update
return;
}
// Get the embedded worksheet part and its cells
using Stream stream = embeddedExcel.GetStream();
using SpreadsheetDocument excelDoc = SpreadsheetDocument.Open(stream, true);
WorkbookPart workbookPart = excelDoc.WorkbookPart!;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
Cell[] cells = worksheetPart.Worksheet.Descendants<Cell>().ToArray();
// Update the embedded worksheet series values
int dataServicesValuesIndex = 0;
foreach (Cell cell in cells)
{
// Only process column B and rows >= 2
string colIndex = GetColumnIndex(cell.CellReference!);
int rowIndex = GetRowIndex(cell.CellReference!);
if (colIndex == "B" && rowIndex >= 2 && dataServicesValuesIndex < chartSeriesValues.Length)
{
// Cells are in Row, Column order, e.g. A1, B2, A2, B2, C1, C2 ...
cell.CellValue = new(chartSeriesValues[dataServicesValuesIndex++]);
}
}
worksheetPart.Worksheet.Save();
// Update the cached chart series values
foreach (NumberingCache? numCache in chartPart.ChartSpace.Descendants<NumberingCache>())
{
List< NumericPoint> points = numCache.Descendants<NumericPoint>().ToList();
try
{
for (int pointIndex = 0; pointIndex < points.Count; pointIndex++)
{
points[pointIndex].NumericValue!.Text = chartSeriesValues[pointIndex].ToString();
}
// Only do this once
break;
}
catch { }
}
// Save the changes to the chart
chartPart.ChartSpace.Save();
}