我有一个包含VBA应用程序的只读Excel工作簿。应用程序保存需要保存在数据库中的所有数据,并且工作簿始终关闭而不保存(通过在BeforeClose中设置ThisWorkbook.Saved = True)。
我有以下问题:
用户在Windows资源管理器中双击工作簿,工作簿将打开。
用户在Windows资源管理器中再次双击工作簿。
Excel提示:“MyWorkbook.xls已经打开。重新打开将导致您所做的任何更改被丢弃。是否要重新打开MyWorkbook.xls?”
如果用户单击“是”,则重新打开工作簿,而不执行已打开的实例的BeforeClose事件处理程序。
这是我的应用程序中的一个问题,因为它意味着BeforeClose事件处理程序中的一些重要清理代码不会被执行。
任何人都可以为此建议VBA解决方案。可能是:
禁止重新打开工作簿的提示。而是默默地使用已经打开的实例。
以某种方式让BeforeClose或其他事件处理程序在原始实例关闭之前运行,因此我可以运行我的清理代码。
更新:
这是Excel 2003。
我可以通过在Workbook_SheetChanged事件处理程序中设置“ThisWorkbook.Saved = True”来消除不需要的提示(VBA应用程序负责保存需要保存在数据库中的任何数据,所以我不在乎关于Excel保存更改)。
然而,这并没有解决我的问题:如果我这样做,那么在资源管理器中双击工作簿会默默地重新打开工作簿,但仍然可以通过“BeforeClose”事件处理程序调用它。
所以重新解释一下这个问题:
更新2
接受BKimmel的回答 - 似乎没有VBA方法可以从工作簿中拦截此事件。
我将实现的解决方案是将应用程序代码移动到XLA加载项中,该加载项在加载工作簿时自动加载(如果尚未加载)。该加载项可以处理Open和BeforeClose事件,并存储执行清理所需的信息。
答案 0 :(得分:2)
您可以使用一些特殊的应用程序事件来处理重新打开工作簿的时间并手动触发Workbook_Close代码:
使用以下代码创建一个名为ApplicationController的类模块:
Public WithEvents CApp As Application
Public CurrentWB as Workbook
Private Sub CApp_WorkbookOpen(ByVal wb As Excel.Workbook)
Dim sPathName As String
sPathName = wb.Name
if sPathName = CurrentWB.Name then CallSomeMethod()
End Sub
然后在你的workbook_load事件上做类似的事情:
Public controller as new ApplicationController
Private Sub Workbook_Open()
set controller.CApp = Excel.Application
set controller.CurrentWB = ThisWorkbook
End Sub
现在,当excel打开一个新工作簿时,您将捕获该事件并运行您的安全方法。如果你愿意,你甚至可以阻止它重新打开工作簿。
答案 1 :(得分:2)
男人......这个似乎很容易,但这很难。我玩弄了我在使用Excel VBA工作7年左右的经验时可以想到的每个事件和应用程序/工作簿属性,我根本无法想出任何优雅的东西......我认为简短的答案是对这个问题没有一个简短的优雅解决方案(如果有的话,我真的很想看到它)。这是个坏消息。
好消息是,我认为 解决问题的方法...... 1更优雅,但需要你要重新考虑一些方法而另一方面是脏的,但可能更简单一点。
更优雅的方式将涉及重新思考和重构您的方法......我认为我在工作中从未遇到过这个问题的原因是,在我的每个方法的最后,工作簿是处于“良好”状态...或换句话说,如果要进行“清理”,则在每种方法结束时完成。如果在此中遇到巨大的开销性能,您可以考虑设置一个布尔标志并限制它是否在每个其他方法的末尾运行。 (即runCleanup = ReturnTrueIfCleanupIsNeeded()If(runCleanup = true)Then CleanupMethod()End If)
更脏的方式会让整个工作簿本身成为一种“ ad-hoc mutex“by
1”将WorkBookOpen事件中的整个(原始)工作簿复制到另一个( temp )工作簿并使用 temp 上的“SaveAs”方法将其放在一个单独的位置。
2)存储(原始)路径。
3)向检查( temp )的工作簿开放事件添加代码,并且A)立即关闭并激活 temp 或B)覆盖自身( temp )然后使用SaveAs
4)在用户保存WorkBook之前捕获事件并将其保存到两者 原始位置和 temp 位置。
5)在WorkBookClose事件中,运行您需要的任何清理并将 temp 保存回原始位置。 />
所有这一切的目的是支持两次打开工作簿时发生的“碰撞” - 实际上,当你第一次打开它时创建一个 temp 工作簿并将其保存在不同的位置(也许你甚至可以隐藏文件?)你确保当用户点击原文时,它不会与他们在应用程序中使用的数据发生冲突。 (当然,这有它自己的问题/问题,例如“如果我不知道用户有权访问哪些路径以保存临时文件”或“如果用户打开 temp XLS文件该怎么办? ......但这就是为什么我称之为 dirt 的方式)“
我知道这很多,特别是如果你不习惯与VBA合作;如果您希望我发布一些代码来帮助您完成上述任何步骤,请发表评论,我会。
感谢有趣的问题。
答案 2 :(得分:1)
您还可以通过以下方式取消提示:
这将重新打开一个没有警告的打开文件。 但请注意,任何更改都会被丢弃!
Application.DisplayAlerts = False
Workbooks.Open (sPath)
Application.DisplayAlerts = True
此解决方案适合我的需求