【问题标题】:Copy/Clone an Excel Chart with EPPlus使用 EPPlus 复制/克隆 Excel 图表
【发布时间】:2021-02-28 06:04:36
【问题描述】:

我在我的项目中使用 EPPlus,我知道您可以复制现有的形状,但是没有复制现有图表的方法。

我已经设置了一个带有模板图表的工作簿,我需要复制和更新系列以指向不同的数据表/集。

我可以不用担心填充数据并且可以创建新图表,但是需要调整大小、位置和样式。克隆图表模板并修改系列和位置以简化代码会容易得多。

目前我使用这种方法:

// wb is an ExcelWorkbook
ExcelWorksheet ws = wb.Worksheets[sheetIdx];
ExcelChart chart = (ExcelChart)ws.Drawings[0];
ExcelChart cc = ws.Drawings.AddChart("Chart " + (i + 2), eChartType.ColumnClustered);

// invoke methods that will position and size new chart

// copy starting chart xml so will have identical styling, series, legend etc
var xml = XDocument.Parse(chart.ChartXml.InnerXml);
XNamespace nsC = "http://schemas.openxmlformats.org/drawingml/2006/chart";
XNamespace nsA = "http://schemas.openxmlformats.org/drawingml/2006/main";

// modify xml to update Category, Title and Values formulas
var fs = xml.Descendants(nsC + "f");
foreach (var f in fs)
{
    f.Value = ws.Cells[f.Value].Offset(chartNumRows + 1, 0).FullAddressAbsolute;
}

// set new chart xml to modified xml.
cc.ChartXml.InnerXml = xml.ToString();

这可行,但有几个缺点。

1)clone的chart.series(我的例子是cc)没有设置,偷看代码这是因为它只在对象构造过程中完成。如果我可以更新此属性,那么我将能够轻松解决第二个问题

2) 我需要删除所有系列并添加新系列,因为系列属性未正确初始化,这比应有的困难。

任何帮助在图表中初始化属性或克隆原始属性的更好方法将不胜感激!

【问题讨论】:

    标签: c# epplus


    【解决方案1】:

    似乎没有内置功能和其他重新加载方法,我可以对 EPPlus 源进行太多更改,因此在找到更好的解决方案之前,我已将以下方法添加到 EPPlus\绘图\ExcelDrawings.cs

    public ExcelChart CloneChart(ExcelChart SourceChart, String NewName)
    {
        // Create clone
        var tempClone = this.AddChart(NewName, SourceChart.ChartType, null);
        tempClone.ChartXml.InnerXml = SourceChart.ChartXml.InnerXml;
    
        // Reload clone
        using (tempClone.Part.Stream = new MemoryStream())
        {
            // Create chart object using temps package and xml
            var chartXmlBytes = Encoding.ASCII.GetBytes(tempClone.ChartXml.OuterXml);
            tempClone.Part.Stream.Write(chartXmlBytes, 0, chartXmlBytes.Length);
            var finalClone = ExcelChart.GetChart(this, tempClone.TopNode);
    
            // Remove old from collection
            var index = _drawingNames[tempClone.Name];
            var draw = _drawings[index];
            for (int i = index + 1; i < _drawings.Count; i++)
                _drawingNames[_drawings[i].Name]--;
            _drawingNames.Remove(draw.Name);
            _drawings.Remove(draw);
    
            // Add new to collection
            finalClone.Name = tempClone.Name;
            _drawings.Add(finalClone);
            _drawingNames.Add(finalClone.Name, _drawings.Count - 1);
    
            // Done
            return finalClone;
        }
    }
    

    【讨论】: