防止工作簿关闭

7

有一个名为ABC.xls的文件,其中包含宏。现在,当我按下Ctrl + M时,将调用该宏的子程序。

这个子程序会打开一个“打开文件”对话框,用户可以选择一个CSV文件。因此,这个宏用于处理和保存CSV文件。

下面是在按下Ctrl + M时调用的代码。

Sub runProperChangeSubroutine()             ' Assigned to shortcut key CTRL + M.
    file_name = ActiveWorkbook.Name         ' Gets the file name.
    Application.Run file_name & "!ChangeSub"
End Sub

当用户关闭工作簿时,我需要测试一个条件,就是每一行的CSV是否有终止字符串。如果没有这个终止字符串,我应该弹出一个消息框告诉用户并阻止工作簿的关闭。

所以我做了以下操作...

Private Sub Workbook_BeforeClose(Cancel As Boolean)
    Dim improperRowNumber As Integer
    Dim BlnEventState As Boolean
    improperRowNumber = returnImproperRow()

    BlnEventState = Application.EnableEvents
    Application.EnableEvents = True

    If improperRowNumber <> -1 Then
        Cancel = True
        MsgBox "The row number " & improperRowNumber & " is not ending with '@/#'. Please correct the row before closing."
    Else
        Call saveCSV
    End If
    Application.EnableEvents = BlnEventState
End Sub

当点击关闭按钮(右上角的X标记,关闭工作簿。我不是关闭Excel),我能看到消息框,但工作簿会关闭。如果行没有正确结束,我希望用户进行一些编辑。请给出建议。

编辑: 由于上述代码不起作用,我在消息框后使用了ThisWorkbook.Saved=False,如下所示...

If improperRowNumber <> -1 Then
    MsgBox "The row number " & improperRowNumber & " is not ending with '@/#'. Please correct the row before closing."
    Application.DisplayAlerts = False
    ThisWorkbook.Saved = False
    Cancel = False
Else

现在它显示“是否保存更改...”消息框。如果我在消息框上单击取消按钮,则工作簿不会关闭。
有没有办法自定义消息框文本或按钮,或隐藏此保存消息框?

在Workbook_BeforeClose中,您不应该能够使用Cancel = True关闭工作簿。也许您在improperRowNumber子程序中有类似于EnableEvents = False的内容? - MarcinSzaleniec
@MarcinSzaleniec,如果禁用应用程序事件,则 msgbox 将不会显示... 但是 OP 的处理程序显然正在运行。 - Mathieu Guindon
你的流程不是很清晰。当用户选择CSV文件时,你会怎么处理它?(像工作簿一样打开它?将其数据导入ABC.xls?还是其他什么?)你把Workbook_BeforeClose代码放在哪个文件中?你试图防止哪个文件关闭?-- 我并不是要挑剔,但这些都是重要的细节,可以帮助人们提供有用的答案,并解决你可能存在的任何错误假设。 - AjimOthy
1
请纠正我,但是既然您已经成功地检测到了没有正确结尾('@/#')的行,为什么您的代码不能自动将其添加到该行中呢? - user2261597
@peakpeak:哈哈哈,有时候我会过于聪明,浪费了太多的时间,而且你也看到了我的分数。 - NJMR
显示剩余2条评论
5个回答

6
如果你必须监控除包含宏的工作簿之外的其他工作簿的关闭,你可以按照以下方式从启用宏的工作簿拦截应用程序级事件:
在ThisWorkbook后面添加(或适应)以下代码:
Option Explicit

Private m_CloseHelper As CloseHelper

Private Sub Workbook_Open()
    Set m_CloseHelper = New CloseHelper
End Sub

添加一个名为CloseHelper的新类模块:
Option Explicit

Private WithEvents m_App As Excel.Application

Private Sub Class_Initialize()
    Set m_App = Excel.Application
End Sub

Private Sub Class_Terminate()
    Set m_App = Nothing
End Sub

Private Sub m_App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
    'Logic goes here, e.g. the code below will prevent the user from closing
    'any workbook other than this one, for as long as this workbook is open.
    If Not Wb Is ThisWorkbook Then
        Cancel = True
        MsgBox "Hello from m_App_WorkbookBeforeClose"
    End If
End Sub

重要的关键字是WithEvents,其原理是Excel应用程序引发的事件现在可以在CloseHelper类中进行编码。
您可以在此处找到更详细的文章:应用程序事件

0

点击右上角的X按钮可以退出Excel应用程序。当应用程序关闭时,不应该期望工作簿仍然保持打开状态。您可以编写一个应用程序事件过程来防止Excel退出,但更好的方法可能是摆脱使用该按钮的坏习惯。找到另一种关闭工作簿的方式。


右上角,我的意思是关闭工作簿,而不是关闭Excel。我会编辑帖子。谢谢。 - NJMR
坦白地说,我怀疑工作簿并没有通过“文件”->“关闭”关闭,消息框弹出后,工作簿仍然关闭了。如果确实是在这种情况下关闭的,那么这个操作应该来自于某些意外运行的其他代码。然而,如果您真的面临Excel应用程序的故障,或者无法控制人们如何关闭工作簿,那么尝试使用应用程序事件(现在您正在使用工作簿事件)执行相同的测试是下一步要尝试的事情。因此,我认为我的答案仍然有效。 - Variatus
1
我认为关闭工作簿或关闭Excel都不应该有影响。无论哪种方式,Workbook_BeforeClose 都会被调用。 - NJMR
这是一个顺序问题。当你退出Excel时,(1)打开的工作簿会被关闭,(2)成功阻止了你的工作簿关闭,(3)Excel无法在没有关闭该工作簿的情况下退出,(4)Excel决定不想在代码和用户指令之间发生差异上卡住,(5)Excel强制关闭工作簿,(6)Excel卸载。故事的寓意是:不要给出相互冲突的指令。在这里了解有关应用程序事件的编程:- http://www.cpearson.com/excel/appevent.aspx - Variatus

0

Workbook_BeforeClose事件仅适用于代码所在的工作簿。当您尝试关闭宏工作簿并将其参数Cancel设置为TRUE时,它会运行,仅在关闭Excel时取消宏工作簿的关闭。打开的csv文件是一个单独的工作簿,Excel会尝试单独关闭它,并生成有关保存csv文件的单独消息。

要自动防止关闭csv文件,您需要使用扩展性编程,在Excel中打开csv文件时添加Workbook_BeforeClose事件。显然,您无法保存带有该事件的文件,但您不需要这样做。模块中的代码将在工作簿仍处于打开状态时运行,防止在未满足条件时关闭;在满足条件后允许成功保存的内容将以.csv格式呈现,这将清除临时添加的代码。

http://www.cpearson.com/excel/vbe.aspx


0

你尝试过关闭所有的插件(或在安全模式下运行Excel)吗?

我问这个问题是因为这个问题可能是由于你的一个插件包含了类似于Excelosaurus答案中描述的应用程序级事件。它可能包含Application.EnableEvents = False甚至Cancel = False并干扰你的代码。

编辑:如果使用受保护的插件,这将特别难以检测,因为代码将在Workbook_BeforeClose之后运行,即使你使用F8逐行调试,你也无法知道它正在做什么。


如果禁用了事件,Workbook_BeforeClose 事件将完全不会运行。 - AjimOthy
应用程序级别的事件在之后运行工作簿级别的事件,因此即使应用程序级别的事件禁用,工作簿级别的事件也将始终运行。 - DecimalTurn
(当然,只要在按下X按钮之前启用了事件。) - DecimalTurn
1
抱歉,我的意思是由于OP指示工作簿事件中运行了一些代码,因此“Application.EnableEvents = False”不能成为问题的根源 - 它会停止所有操作。理论上讲,一个插件可能会设置“Cancel = False”,虽然这很奇怪。 - AjimOthy
你也可以这样做: Application.EnableEvents = False Wb.Close SaveChanges:=False Application.EnableEvents = True 这将阻止Workbook_BeforeClose第二次运行。 - DecimalTurn

0

您可以在工作簿宏中使用此代码

Private Sub Workbook_BeforeClose(Cancel As Boolean)
ActiveWorkbook.Save
Workbooks.Open Application.ActiveWorkbook.FullName
End Sub

正如您所看到的,它基本上在关闭时保存并打开工作簿


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接