c# - 复制 Excel 图表并将其移动到另一个工作表

标签 c# excel charts excel-interop

我正在使用 C# Excel 互操作,我想从一个工作表创建图表的副本,但我想在另一张工作表上创建该副本。我尝试了以下方法:

Excel.ChartObject chartTemplate = (Excel.ChartObject)sheetSource.ChartObjects("chart 1");
object o = chartTemplate.Duplicate();
Excel.ChartObject chart = (Excel.ChartObject)sheetSource.ChartObjects("chart 2");
chart.Name = "Skew" + expiry.ToString("MMMyy");
range = sheetDestination.Range["T" + chartRowCoutner.ToString()];

chart.Chart.Location(Excel.XlChartLocation.xlLocationAsObject, range);

但是当我尝试这样做时,最后一行抛出错误:

An unhandled exception of type 'System.Exception' occurred in projectname.exe

Additional information: Error reading Excel file C:\ ...the file path...\template.xlsx: Value does not fall within the expected range.

我也试过传递一个工作表而不是一个范围:

chart.Chart.Location(Excel.XlChartLocation.xlLocationAsObject, sheetDestination);

但这给出了同样的错误。我不明白错误的原因或如何修复它/绕过它。

我尽量避免将剪贴板带入这里,但即使我尝试复制和粘贴,我仍然只能将其作为图像粘贴,这确实不理想:

Excel.ChartArea chartArea = chart.ChartArea;
chartArea.Copy();
range = sheetDestination.Range["T" + chartRowCoutner.ToString()]; // Note that chart is not on the sheet sheetDestination
range.PasteSpecial(Excel.XlPasteType.xlPasteAll);

我现在能想到的唯一其他解决方案是在 VBA 中执行此操作,然后通过互操作执行宏。但可以肯定的是,只需使用互操作而不使用剪贴板,它就可以以一种干净的方式完成。

最佳答案

您已经有了解决方案,但我不会给您一天的鱼,而是给您一个正确的答案,这将帮助您完成任何 C# Excel 编码任务。

Excel 的 C# Interop 模型与 VBA Excel 模型几乎相同。

这意味着将 VBA 录制的宏转换为 C# 是微不足道的。让我们通过将图表移动到不同的工作表这样的练习来尝试一下。


在 Excel 的“开发人员”选项卡中,单击“录制宏”> 右键单击​​“图表”> 选择“移动图表”> 选择“对象在:Sheet2”> 单击“确定”> 单击“停止宏录制”。

enter image description here

要查看录制的宏,请按 Alt + F11 调出 VB 编辑器:

enter image description here

请看上面的屏幕截图,VBA 如何向您显示 Location() 的第二个参数是 Name,它实际上是一个字符串参数...

让我们将此 VBA 宏转换为 C#:

enter image description here

@Ama 编辑

下面的建议已经过时了,实际上没有必要担心释放 COM 对象,这是在 RELEASE 模式下自动完成的 (DEBUG mode does not)。参见 Hans Passant's answer to "Clean up Excel Interop Objects with IDisposable" .


这里的诀窍是:永远不要对 com 对象使用 2 个点。

注意我是怎么写的:

var sheetSource = workbookWrapper.ComObject.Sheets["Sheet1"];

但是那有两个点,所以我改为这样写:

var workbookComObject = workbookWrapper.ComObject;
var sheetSource = workbookComObject.Sheets["Sheet1"];

引用:How do I properly clean up Excel interop objects?

您将在上面的 QA 中看到 AutoReleaseComObject 代码,项目类似于 VSTOContrib使用。

完整代码如下:

using Microsoft.Office.Interop.Excel;
...
var missing = Type.Missing;
using (AutoReleaseComObject<Microsoft.Office.Interop.Excel.Application> excelApplicationWrapper = new AutoReleaseComObject<Microsoft.Office.Interop.Excel.Application>(new Microsoft.Office.Interop.Excel.Application()))
{
    var excelApplicationWrapperComObject = excelApplicationWrapper.ComObject;
    excelApplicationWrapperComObject.Visible = true;

    var excelApplicationWrapperComObjectWkBooks = excelApplicationWrapperComObject.Workbooks;
    try
    {
        using (AutoReleaseComObject<Workbook> workbookWrapper = new AutoReleaseComObject<Workbook>(excelApplicationWrapperComObjectWkBooks.Open(@"C:\Temp\ExcelMoveChart.xlsx", false, false, missing, missing, missing, true, missing, missing, true, missing, missing, missing, missing, missing)))
        {
            var workbookComObject = workbookWrapper.ComObject;
            Worksheet sheetSource = workbookComObject.Sheets["Sheet1"];
            ChartObject chartObj = (ChartObject)sheetSource.ChartObjects("Chart 3");
            Chart chart = chartObj.Chart;
            chart.Location(XlChartLocation.xlLocationAsObject, "Sheet2");

            ReleaseObject(chart);
            ReleaseObject(chartObj);
            ReleaseObject(sheetSource);

            workbookComObject.Close(false);
        }
    }
    finally
    {
        excelApplicationWrapperComObjectWkBooks.Close();
        ReleaseObject(excelApplicationWrapperComObjectWkBooks);

        excelApplicationWrapper.ComObject.Application.Quit();
        excelApplicationWrapper.ComObject.Quit();
        ReleaseObject(excelApplicationWrapper.ComObject.Application);
        ReleaseObject(excelApplicationWrapper.ComObject);

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();    
    }
}

private static void ReleaseObject(object obj)
{
    try
    {
        while (System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0);
        obj = null;
    }
    catch (Exception ex)
    {
        obj = null;
        Console.WriteLine("Unable to release the Object " + ex.ToString());
    }
}

我知道释放所有对象,使用 GC.Collect 并且在分配时不使用两个点似乎超出了顶部但至少当我退出 Excel 实例时进程被释放,我不必以编程方式终止 Excel过程!

引用:Microsoft KB: Office application does not quit after automation from .NET client

关于c# - 复制 Excel 图表并将其移动到另一个工作表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33782622/

相关文章:

c# - 如何使用 C# xaml 以编程方式设置数据绑定(bind)

python - 使用 cx_freeze 和 PyInstaller 在 Python 中创建 .exe 文件时出错(包括 xlwings)

xml - 在Excel UI Ribbon中动态填充dynamicMenu

java - Vaadin 14.1.17 - 获取图表中选择的元素 - Java

c# - 分配给 Lazy<> 时的反协方差问题

c# - 来自队列的大对象堆和字符串对象

Java:使用 LWJGL 建模/渲染交互式六边形图形/图表

android - MPAndroidChart - 使用 CombinedChart 的水平条

c# - 将 byte[] 转换为文件流而不写入磁盘

excel - 如何复制条件格式而不从条件格式单元格复制规则?