复制一个 Excel 图表并将其移动到另一个 sheet
Duplicate an Excel chart and move it to another sheet
我正在使用 C# Excel 互操作,我想从一个 sheet 创建一个图表的副本,但我想在另一个 sheet 上创建该副本。我尝试了以下方法:
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.
我也试过传递 sheet 而不是范围:
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 中执行此操作,然后通过互操作执行宏。但可以肯定的是,它可以在没有剪贴板的情况下使用互操作以干净的方式完成。
来自此处的 MSDN 文档:
https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.chart.location.aspx
它声明对于类型对象的名称参数:
姓名
类型:System.Object
如果 where 是 xlLocationAsObject,则嵌入图表的 sheet 的名称或如果 Where 是 xlLocationAsNewSheet.
,则新 sheet 的名称
这与同一链接页面底部的示例有些误导。从给出的示例中可以看出,您实际上应该传递 sheet 名称的字符串。下面复制了示例中的相关行(该示例用于复制到 new sheet):
chart1.Location(Excel.XlChartLocation.xlLocationAsNewSheet,
"Sales");
所以,为了移动到现有的 sheet,我会这样做:
chart1.Location(Excel.XlChartLocation.xlLocationAsObject,
"ExistingSheetName");
不要传递范围、工作簿或工作sheet 对象。尝试使用 sheet 名称的 字符串 。
现在,在上面链接的同一个 MSDN 文档页面中,如果您想在将图表移动到另一个页面后重新定位图表 sheet,还有其他说明,为方便起见在此处重复:
如果要将图表移动到 sheet 上的另一个位置,请使用 P:Microsoft。Office.Interop.Excel.ChartArea.Top 属性 和 P:Microsoft.Office.Interop.Excel.ChartArea.Left 属性 的图表区域。使用ChartArea属性.
可以得到Chart的ChartArea对象
如果您要将图表移动到现有 sheet,请注意不要将图表与现有数据重叠。如果是这样,您将不得不单独编写代码。
您已经找到了解决方案,但我不会给您一天的鱼,而是给您一个正确的答案,这将帮助您完成任何 C# Excel 编码任务。
Excel 的 C# Interop 模型几乎与 VBA Excel 模型相同。
这意味着将 VBA 记录的宏转换为 C# 是微不足道的。让我们通过将图表移动到不同的 sheet.
这样的练习来尝试一下
在 Excel 的“开发人员”选项卡中单击“录制宏”> 右键单击“图表”> select“移动图表”> 选择对象:Sheet2 > 单击“确定”> 单击“停止宏录制”。
要查看录制的宏,请按 Alt + F11 调出 VB 编辑器:
请参阅上面的屏幕截图,VBA 向您展示 Location() 的第二个参数是 Name,它实际上是一个 字符串参数...
让我们将此 VBA 宏转换为 C#:
@Ama 编辑
以下建议已过时,实际上无需担心释放 COM 对象,这是在 RELEASE 模式下自动完成的 (DEBUG mode does not). See 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 中看到像 VSTOContrib 这样的项目使用的 AutoReleaseComObject
代码。
完整代码如下:
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
这不是您所问问题的答案,但可能会有所收获
如果您正在制作副本并针对不同的变体进行编辑,这不是解决方案
如果您真的只是复制图表,那么我建议改用 Excel 的 "Camera" 函数。它基本上将 window 创建到另一个 sheet - 您可以通过编程方式执行此操作并且有据可查,但是 excel 的一个鲜为人知的功能我认为如果我不这样做我会失职指出。
-E
如果您想要进行编辑并且问题仍然悬而未决,请在评论中告诉我 - 我已经这样做了,我只需要回顾一下我的工作簿并确切地了解我是如何做到的。
'相机选项很好,因为它没有 'recalculate' 数据 - 所以我想它运行得更快;大型工作簿中的一个问题。
我正在使用 C# Excel 互操作,我想从一个 sheet 创建一个图表的副本,但我想在另一个 sheet 上创建该副本。我尝试了以下方法:
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.
我也试过传递 sheet 而不是范围:
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 中执行此操作,然后通过互操作执行宏。但可以肯定的是,它可以在没有剪贴板的情况下使用互操作以干净的方式完成。
来自此处的 MSDN 文档:
https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.chart.location.aspx
它声明对于类型对象的名称参数:
姓名 类型:System.Object 如果 where 是 xlLocationAsObject,则嵌入图表的 sheet 的名称或如果 Where 是 xlLocationAsNewSheet.
,则新 sheet 的名称这与同一链接页面底部的示例有些误导。从给出的示例中可以看出,您实际上应该传递 sheet 名称的字符串。下面复制了示例中的相关行(该示例用于复制到 new sheet):
chart1.Location(Excel.XlChartLocation.xlLocationAsNewSheet,
"Sales");
所以,为了移动到现有的 sheet,我会这样做:
chart1.Location(Excel.XlChartLocation.xlLocationAsObject,
"ExistingSheetName");
不要传递范围、工作簿或工作sheet 对象。尝试使用 sheet 名称的 字符串 。
现在,在上面链接的同一个 MSDN 文档页面中,如果您想在将图表移动到另一个页面后重新定位图表 sheet,还有其他说明,为方便起见在此处重复:
如果要将图表移动到 sheet 上的另一个位置,请使用 P:Microsoft。Office.Interop.Excel.ChartArea.Top 属性 和 P:Microsoft.Office.Interop.Excel.ChartArea.Left 属性 的图表区域。使用ChartArea属性.
可以得到Chart的ChartArea对象如果您要将图表移动到现有 sheet,请注意不要将图表与现有数据重叠。如果是这样,您将不得不单独编写代码。
您已经找到了解决方案,但我不会给您一天的鱼,而是给您一个正确的答案,这将帮助您完成任何 C# Excel 编码任务。
Excel 的 C# Interop 模型几乎与 VBA Excel 模型相同。
这意味着将 VBA 记录的宏转换为 C# 是微不足道的。让我们通过将图表移动到不同的 sheet.
这样的练习来尝试一下在 Excel 的“开发人员”选项卡中单击“录制宏”> 右键单击“图表”> select“移动图表”> 选择对象:Sheet2 > 单击“确定”> 单击“停止宏录制”。
要查看录制的宏,请按 Alt + F11 调出 VB 编辑器:
请参阅上面的屏幕截图,VBA 向您展示 Location() 的第二个参数是 Name,它实际上是一个 字符串参数...
让我们将此 VBA 宏转换为 C#:
@Ama 编辑
以下建议已过时,实际上无需担心释放 COM 对象,这是在 RELEASE 模式下自动完成的 (DEBUG mode does not). See 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 中看到像 VSTOContrib 这样的项目使用的 AutoReleaseComObject
代码。
完整代码如下:
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
这不是您所问问题的答案,但可能会有所收获
如果您正在制作副本并针对不同的变体进行编辑,这不是解决方案
如果您真的只是复制图表,那么我建议改用 Excel 的 "Camera" 函数。它基本上将 window 创建到另一个 sheet - 您可以通过编程方式执行此操作并且有据可查,但是 excel 的一个鲜为人知的功能我认为如果我不这样做我会失职指出。
-E
如果您想要进行编辑并且问题仍然悬而未决,请在评论中告诉我 - 我已经这样做了,我只需要回顾一下我的工作簿并确切地了解我是如何做到的。
'相机选项很好,因为它没有 'recalculate' 数据 - 所以我想它运行得更快;大型工作簿中的一个问题。