复制Excel图表并将其移至另一张表

时间:2015-11-18 14:27:54

标签: 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);

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

  

projectname.exe

中出现未处理的“System.Exception”类型异常      

附加信息:读取Excel文件时出错C:\ ...文件路径... \ template.xlsx:值不属于   预期范围。

我也试过传递一张表而不是范围:

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中执行此操作,然后通过互操作执行宏。但肯定只需使用没有剪贴板的互操作即可以干净的方式完成。

3 个答案:

答案 0 :(得分:3)

您已经获得了解决方案,但我没有给您一天的鱼,我会给您一个正确的答案,可以帮助您完成任何C#Excel编码任务。

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

这意味着将VBA 录制的宏转换为C#非常简单。让我们尝试这个练习,比如将图表移动到不同的工作表。

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

enter image description here

要查看录制的宏,请按 Alt + F11 以显示VB编辑器:

enter image description here

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

允许将此VBA宏转换为C#:

enter image description here

这里的诀窍是:

  

切勿对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?

您将看到上述质量检查中的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实例时,进程被释放,我不# 39;必须以编程方式杀死Excel进程!!

参考:Microsoft KB: Office application does not quit after automation from .NET client

答案 1 :(得分:2)

来自MSDN文档:

https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.chart.location.aspx

它声明对于object类型的Name参数:

名称 键入:System.Object 嵌入图表的工作表的名称,如果在哪里是xlLocationAsObject,或者新工作表的名称,如果在哪里是xlLocationAsNewSheet。

这在同一链接页面底部的示例中有些误导。从给出的示例中可以看出,您应该实际传递工作表名称的字符串。下面复制了示例中的相关行(示例是复制到 new 表):

chart1.Location(Excel.XlChartLocation.xlLocationAsNewSheet, 
    "Sales");

所以,为了转移到现有的工作表,我会这样做:

chart1.Location(Excel.XlChartLocation.xlLocationAsObject, 
        "ExistingSheetName");

不要传递范围,工作簿或工作表对象。尝试使用工作表名称的字符串

现在,从上面链接的同一个MSDN文档页面中,如果您想要在将页面移动到另一个工作表后重新定位页面中的图表,还有其他说明,为方便起见,在此重复:

如果要将图表移动到工作表上的其他位置,请使用P:Microsoft.Office.Interop.Excel.ChartArea.Top属性和P:Microsoft.Office.Interop.Excel.ChartArea.Left ChartArea的财产。您可以使用ChartArea属性获取Chart的ChartArea对象。

如果您要将图表移动到现有工作表,请注意不要将图表与现有数据重叠。如果是这样,你将不得不单独编码。

答案 2 :(得分:1)

这不是你提出的问题的答案,但可能是富有成效的

如果您正在制作副本并针对不同的变体进行编辑,那么这不是解决方案

如果您真的只是复制图表,那么我建议您使用Excel&#34;相机&#34;功能而不是。它基本上创建了一个窗口到另一个工作表 - 你可以通过编程方式完成这个并且它有很好的文档记录,但是excel的一个鲜为人知的特性我认为如果我没有指出我就会失职。 / p>

-E

如果您正在进行编辑和编辑这个问题仍然是公开的让我知道在评论中 - 我之前已经完成了这个,我只需要回顾一下我的工作簿,看看我是如何做到的。

&#39;相机选项很不错,因为它没有重新计算&#39;数据 - 所以我想它运行得更快;大型工作簿中的一个问题。