NET 6/7/8 - OpenXML 更新图表

问题描述 投票:0回答:2

我正在开发一个应用程序,我将在其中填写一些文本并想要更新一些图表。

到目前为止,我已经使用 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# .net ms-word openxml
2个回答
1
投票

您需要更新图表部分中的

<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())

0
投票

除了 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();
}
© www.soinside.com 2019 - 2024. All rights reserved.