How to detect when a workbook is closing?
This is an evolution of my 1st Answer - it detects the edge case problem by comparing the ActiveWindow.Caption against ThisWorkbook.Name so it can detect that issue and deal with it. It's not the most elegant solution but I believe it works.
All Code in the Workbook most of it in DeActivate
Public ByeBye As String
Private Sub Workbook_BeforeClose(Cancel As Boolean)
ByeBye = "B4C"
End Sub
Private Sub Workbook_Deactivate()
If ByeBye = "B4C" Then
If ActiveWindow.Caption = ThisWorkbook.Name Then
If ThisWorkbook.Saved Then
MsgBox "No problem - Closing after Saving"
Else
MsgBox "No problem - Closing without Saving"
End If
Else
If ThisWorkbook.Saved Then
MsgBox "No problem - New Workbook Activation"
Else
MsgBox "Oops Try Again You Cannot Activate '" & ActiveWindow.Caption & "' until '" & ThisWorkbook.Name & "' has completed processing & IT HAS NOW COMPLETED", vbOKOnly, "Hiding"
ThisWorkbook.Activate
End If
End If
Else
MsgBox "No problem - Just Hiding"
End If
ByeBye = "Done"
End Sub
Private Sub Workbook_Open()
ByeBye = "OPENED"
End Sub
In response to comment about saving I tested this for 7 possible combinations as follows
1) Closing without Edits - No Saving Involved ... MsgBox Prompted with ... No problem - Closing after Saving
2) Not closing - Just Switch Workbook - Whether Edited or Not ... MsgBox Prompted with ... No problem - Just Hiding
3) Not closing - Switch Workbook - After Edit & Cancel ... MsgBox Prompted with ... Oops Try Again …
4) Closing and saving ... MsgBox Prompted with ... No problem - Closing after Saving
5) Closing and Saving after a prior Cancel ... MsgBox Prompted with ... No problem - Closing after Saving
6) Closing but Not Saving ... MsgBox Prompted with ... No problem - Closing without Saving
7) Closing but not Saving after a prior Cancel ... MsgBox Prompted with ... No problem - Closing without Saving
I think trying to cancel the close event is the wrong approach for what you are trying to do. A better approach would be to have a function that is only called when the workbook is actually closing.
Thank you for the comments regarding OnTime not being called while the dialog is open as that pointed me in the right direction. What we need to test is the time between the workbook deactivation and the closing of either the workbook itself or the save dialog. Using the Excel.Application.OnTime function to set this close time means this is possible as it can be delayed until the save dialogue has closed.
Once we have this time, a simple comparison to the deactivation time allows us to decide whether to call the exit function or not.
I initially ran into issues with the workbook reopening to run the .OnTime procedure, so an artificial delay needs to be added into the Deactivation function so the workbook hasn't closed until the close time has been set. Using the code from here - Delay Macro to allow events to finish we can accomplish this.
In ThisWorkbook
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Excel.Application.OnTime EarliestTime:=Now, Procedure:="SetCloseTime"
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If Timer < CloseTime + 0.2 Then Call CloseProcedure
End Sub
Private Sub Workbook_Deactivate()
Delay (0.3)
If Timer < CloseTime + 0.4 Then Call CloseProcedure
End Sub
In a module
Option Explicit
Public CloseTime As Single
Function SetCloseTime()
CloseTime = Timer
End Function
Function Delay(Seconds As Single)
Dim StopTime As Single: StopTime = Timer + Seconds
Do While Timer < StopTime
DoEvents
Loop
End Function
Function CloseProcedure()
MsgBox "Excel is closing"
End Function
The .OnTime seems to run within one second cycles which dictates the length of the delay and the time difference test has a little leeway added with an additional 1/10th of a second (which I found necessary). These timings could potentially need slight tweaking but have so far worked for me with the different scenarios when closing the workbook.