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

我正在使用C#Excel互操作,我想创build一个图表的副本,但我希望在另一个工作表上的副本。 我已经尝试了以下内容:

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”types的exception

附加信息:读取Excel文件时出错C:\ …文件path… \ 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); 

我现在唯一能想到的其他解决scheme是在VBA中执行此操作,然后通过interop执行macros。 但是可以肯定的是,只需使用不带剪贴板的互操作即可完成。

您已经有了解决scheme,但是我不会给您一天的时间,我会给您一个适当的答案,这将有助于您完成任何C#Excel编码任务。

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

这意味着将VBA 录制的macros转换为C#是很简单的。 让我们尝试一下这样的练习,就像将图表移动到不同的工作表一样。


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

在这里输入图像说明

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

在这里输入图像说明

请参阅上面的屏幕截图,VBA如何显示Location()的第二个参数是Name ,实际上是一个string参数

让我们把这个VBAmacros转换成C#:

在这里输入图像说明

这里的诀窍是:

切勿使用com对象使用2个点。

请注意我可以怎么写:

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

但有两个点,所以我写这个:

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

参考: 如何正确清理Excel互操作对象?

你会在上面的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,而不是使用两个点,当分配似乎在顶部, 至less当我退出Excel的实例过程被释放,我不必以编程方式杀死Excel进程!

参考: Microsoft KB:Office应用程序不会退出.NET客户端自动化后

从这里的MSDN文档:

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

它指出对于types对象的Name参数:

名称types:System.Object如果Where是xlLocationAsObject,或者如果Where是xlLocationAsNewSheet,则为新工作表的名称,其中embedded图表的工作表的名称。

这与相同链接页面底部的示例有些误导。 从给出的例子中可以看出,实际上应该传递一个表名的string。 下面将复制示例中的相关行(该示例用于复制到新工作表):

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

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

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

不要传递范围,工作簿或工作表对象。 尝试表单名称的string

现在,从上面链接的同一个MSDN文档页面中,如果您想要在页面内重新定位图表时,将其移至另一个表格中,还有其他说明,为便于说明,请在此处重复说明:

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

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

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

如果您正在制作一个副本并对其进行编辑,那么这不是一个解决scheme

如果你真的只是复制图表,我build议使用Excel的“相机”function。 它基本上创build了一个窗口到另一个工作表 – 你可以通过编程的方式来做到这一点,它是有据可查的,但是有一点我知道的特点,我认为如果我没有指出,我会失去工作。

-E

如果您正在进行编辑并且问题仍然存在,请让我知道,在评论中 – 我已经完成了这一步,然后我只需要回顾一下工作簿,看看我是如何做到的。

“相机选项很好,因为它不会”重新计算“数据 – 所以我想它的运行速度更快; 在大型工作簿的关注。