另外,大家好,
我正在通过C#中的Interop进行Excel自动化,我希望在工作簿关闭时得到通知。但是,工作簿上没有Close事件,也没有应用程序上的Quit事件。
以前有人这样做过吗? 如何编写一段对正在关闭的工作簿做出反应的代码(仅在工作簿真正关闭时才执行)?理想情况下应该在之后执行关闭工作簿,因此我可以依赖该文件来反映所有更改。
到目前为止我发现的详细信息:
有一个BeforeClose()事件,但是如果有未保存的更改,则会在询问用户是否保存它们之前引发此事件,所以目前我可以处理该事件,我没有最终文件和我不能释放COM对象,这两件事我都需要/做。我甚至不知道工作簿是否会被关闭,因为用户可能会选择中止关闭。
然后有一个BeforeSave()事件。因此,如果用户选择“是”来保存未保存的更改,则在 BeforeClose()之后执行BeforeSave()。但是,如果用户选择“中止”,然后点击“文件 - >保存”,则执行完全相同的事件顺序。此外,如果用户选择“否”,则根本不执行BeforeSave()。只要用户没有点击任何这些选项,情况就同样如此。
答案 0 :(得分:4)
我使用类似轮询的方法创建了一个hack,它可以工作:
鉴于要观察的工作簿,我创建了一个线程,它定期尝试在工作簿集合中查找该工作簿。
(DisposableCom类是我目前properly cleanup COM objects的解决方案。)
Excel.Application app = wbWorkbook.Application;
string sWorkbookName = wbWorkbook.Name;
Thread overseeWorkbooksThread = new Thread(new ThreadStart(
delegate()
{
bool bOpened = false;
Excel.Workbooks wbsWorkbooks = app.Workbooks;
using (new DisposableCom<Excel.Workbooks>(wbsWorkbooks))
{
while (true)
{
Thread.Sleep(1000);
if (wbsWorkbooks.ContainsWorkbookProperly(sWorkbookName))
bOpened = true;
else
if (bOpened)
// Workbook was open, so it has been closed.
break;
else
{
// Workbook simply not finished opening, do nothing
}
}
// Workbook closed
RunTheCodeToBeRunAfterWorkbookIsClosed();
}
}));
overseeWorkbooksThread.Start();
“ContainsWorkbookProperly”扩展方法如下所示:
public static bool ContainsWorkbookProperly(this Excel.Workbooks excelWbs,
string sWorkbookName)
{
Excel.Workbook wbTemp = null;
try
wbTemp = excelWbs.Item(sWorkbookName);
catch (Exception)
{
// ignore
}
if (wbTemp != null)
{
new DisposableCom<Excel.Workbook>(wbTemp).Dispose();
return true;
}
return false;
}
如果有更简单或更好的解决方案,我仍然会感兴趣。
答案 1 :(得分:3)
这不是我的代码,但这对我有用:
https://gist.github.com/jmangelo/301884
复制粘贴:
[element0, element1, ..., elementN]
new Array(element0, element1[, ...[, elementN]])
new Array(arrayLength)
当我使用它时,我将其从返回工作簿的名称更改为对工作簿的引用。
答案 2 :(得分:0)
在工作簿上安排SyncContext操作。停用。关闭工作簿时以及另一个工作簿获得焦点时,都会引发此事件。
通常,在Deactivate处理程序中,您无法检查工作簿是否已关闭或只是失去焦点,但是可以在SyncContext上加入一个操作,使其在事件发生后立即执行。在该操作中,您可以检查工作簿是否仍然存在,并在不存在的情况下执行代码。
这是一个例子:
// put a syncContext instance somewhere you can reach it
static SynchronizationContext syncContext = SynchronizationContext.Current ?? new System.Windows.Forms.WindowsFormsSynchronizationContext();
// subscribe to workbook deactivate
workbook.Deactivate += workbook_Deactivate;
[DebuggerHidden]
private void workbook_Deactivate()
{
// here, the workbook is still alive, but we can schedule
// an action via the SyncContext which will execute
// right after the deactivate event is completed. At that
// point, the workbook instance (RCW) will no longer be usable
// meaning that the workbook has been closed
syncContext.Post(x =>
{
try
{
// will throw if workbook is gone
workbook.Path.ToString();
}
catch
{
// handle workbook closed
}
}, null);
}
答案 3 :(得分:-1)
你能同时使用这两个活动吗? On BeforeClose()设置一个标志,然后BeforeSave()查看是否设置了标志。但是,如果触发BeforeClose()而没有BeforeSave(),则需要一种方法来重置它。不确定是否还有其他方法可以帮助解决这个问题。
编辑:看起来你已经用“完全相同的事件顺序执行”来覆盖这个。但是,如果你能找到一种方法来重置它(另一个“取消”事件?)它可能会有效。