Run auto_close when there are several workbooks open - excel

I am trying to run VBA code whenever a workbook closes
I tried both Private Sub auto_close() inside the Module and Private Sub Workbook_BeforeClose(Cancel As Boolean) inside "ThisWorkbook". They both work when there is a single file open but not if there are multiple files open.
The problem is that it seems that if there are several Excel files (workbooks) open at the same time, this seems to not work. Only if I close them to the point when there is only a single file open, then the code is executed

Prevent Closing Workbooks Using a Class
Note that this code goes into three different modules. I'm a newb when it comes to classes, I've only once built something similar. I don't understand when or why it stops (but sometimes it does). So if you run into trouble, ask another question (you have a class related code now) and put Class in the title and hopefully someone more qualified could answer.
The only possibly new thing for you is the third code, which has to go into a class module. Insert a new class module (Class1) and in the properties window change Class1 to wbCloser. Copy the codes into the appropriate modules and save the workbook. Then run AppEventsInit in the standard module.
Note that every time you open this workbook, AppEventsInit will run (Workbooks_Open).
ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
AppEventsInit
End Sub
Standard Module e.g. Module1
Option Explicit
Public AppEvent As New wbCloser ' Class Name ('wbCloser')
Sub AppEventsInit()
Set AppEvent.App = Application
End Sub
Class Module: Class1 renamed to wbCloser
Option Explicit
Public WithEvents App As Application
Private Sub App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
Dim msg As Variant
msg = MsgBox("The workbook '" & Wb.Name _
& "' is trying to close. Will you allow it?", vbYesNo)
If msg = vbYes Then
'Cancel = False ' by default
MsgBox "You allowed closing of '" & Wb.Name & "'."
Else
Cancel = True
MsgBox "You prevented closing of '" & Wb.Name & "'."
End If
End Sub
Private Sub Class_Initialize()
' Remove if it doesn't work.
Application.Speech.Speak "Workbook Clozer Initialized"
End Sub

Try this:
After opening the VBA and inserting a module type in this:
Sub CloseAllActiveWorkbooks()
Workbooks.close
End sub

Related

How can I manually trigger the Activate event of a worksheet in VBA?

I have a worksheet with:
Private Sub Worksheet_Activate()
MsgBox "Ran"
End Sub
I have a button on my toolbar that I made. What I want it to do is trigger this method on the currently selected WorkSheet.
I figured I could do Call ActiveWorksheet.Activate or Call Worksheet.Activate but while these seem to execute without errors, the method is not called.
As a workaround I considered adding a public DoActivate method, but it seems a bit lame and I would likely have to fiddle with CallByName to get it to work (and developers would have to remember to implement this method on every worksheet).
Is there a reason why my Activate method is not calling manually via the above code, or a suitable workaround to get what I'm looking for?
Move your code to a new Sub called OnActivate
Private Sub Worksheet_Activate()
OnActivate
End Sub
Private Sub OnActivate() 'or Public if you call from another module
MsgBox "Ran"
End Sub
The Worksheet_Activate() event handler can be called manually from inside the module by Worksheet_Activate like any other sub (although this is IMO not a nice way to do it)
If you want to ensure all worksheets have the same method, then you can make them Implement an interface: e.g.
Class module: IActivateHandler
Public Sub OnActivate()
End Sub
Then in Sheet1, 2, 3 etc:
Implements IActivateHandler
Private Sub IActivateHandler_OnActivate()
MeOnActivate
End Sub
Private Sub Worksheet_Activate()
MeOnActivate
End Sub
Private Sub MeOnActivate()
MsgBox "Ran"
End Sub
And the button:
Private Sub Button1_Click()
Dim sheetToCall As IActivateHandler
' Debug.Assert TypeOf ActiveSheet Is IActivateHandler
Set sheetToCall = ActiveSheet 'gets the IActivateHandler of the sheet, ensuring it is defined. Will error if it isn't
sheetToCall.OnActivate 'runs the IActivateHandler_OnActivate() method of sheet1
End Sub
You can call the activating event of any active sheet (without knowing its name) in this way:
Create the next event in ThisWorkbook code module. Or, simple copy the following code. Take care that such an event does not already exist:
Public Sub Workbook_SheetActivate(ByVal Sh As Object)
MsgBox Sh.Name & " activated..."
'the necessary code here...
End Sub
Then, call it from a standard module in the next way:
ThisWorkbook.Workbook_SheetActivate ActiveSheet
If you want excepting some sheets, you can adapt the event code to do it:
If sh.Name <> "MySheetName" then
MsgBox Sh.Name & " activated..."
'the necessary code here...
End if
If many sheets should be excepted, an array of sheet names should be built and use Application.Match to differentiate between the sheets to use their event and the other ones.
Edited:
If you need a piece of code written in an add-in (or any macro enabled workbook), able to catch the Activate event of a sheet in any (other) open workbook, you should proceed in the next way:
Copy the next declaration on top of the add-in ThisWorkbook code module (in the declarations area):
Public WithEvents appEvHandler As Application
In the same code module, copy the next code:
Private Sub appEvHandler_SheetActivate(ByVal sh As Object)
If sh.Parent.Name <> ThisWorkbook.Name Then
MsgBox sh.Parent.Name & " workbook, sheet " & sh.Name & " activated..."
Else
Debug.Print "changed in this workbook..."
End If
End Sub
Copy also the next Sub, which will activate the event:
Sub activateAppEvHandler()
Set appEvHandler = Application 'It can be placed in `Workbook_Open` event to be run when workbook opens
End Sub
If you want to inactivate it (for some reason...), use the next Sub:
Sub InactivateAppEvHandler()
Set appEvHandler = Nothing
End Sub
Please, test it and send some feedback. I must confess I am not sure I correctly understood what you need. I was asking for a scenario to be followed, but I tried imagining that this is what you want...

Excel VBA - Calling a Sub int a Workbook from my Personal Macro Workbook

I maintain a personal workbook of Subs that I use via a Custom toolbar. I am working on a project in which there is a sub that I want to call regularly from the toolbar. But I save the workbook under development several times a day to maintain a go-back capability. Since the workbook name changes each time, the toolbar is tied to the Sub in an out of date workbook.
I want to keep a Sub in my personal macro workbook that calls a sub in the currently open workbook - any ideas how? I made it public but that didn't do it. I tried transferring the code across but it refers to variables (even global) that aren't accessible to the personal macro workbook.
Try the next approach, plese:
Create the macro to be called in the workbook which will be the active one:
Public Sub myMacroOtherWorkbook()
MsgBox "Hello from other workbook!"
End Sub
Call it from your Personal Macro Workbook, in this way:
Sub testCallMacroOtherWb()
Application.Run ("'" & ActiveWorkbook.Name & "'!myMacroOtherWorkbook")
End Sub
Version 2, in case you need to pass a parameter:
Transform the above Sub in:
Public Sub myMacroOtherWorkbook(Optional strCall As String)
MsgBox "I will bring a " & IIf(strCall = "", "", strCall) & " horse."
End Sub
Call it in this way:
Sub testCallMacroOtherWb()
Application.Run "'" & ActiveWorkbook.Name & "'!myMacroOtherWorkbook", "black"
End Sub
Note: The first call will still work, but the horse will be of no color... :)

How to create an App Events object without using ThisWorkbook?

Inherited a rather large selection of spreadsheets for multiple clients.
Some of these have multiple ThisWorkbooks (e.g. ThisWorkbook, ThisWorkbook2, etc...).
Trying to put some event code check to automatically run when the workbook is opened. Either Workbook or App Events could be a solution.
The recommendation from http://www.cpearson.com/Excel/AppEvent.aspx suggests adding something like the following code to ThisWorkbook.
Private XLApp As CExcelEvents
Private Sub Workbook_Open()
Set XLApp = New CExcelEvents
End Sub
The issue is that if there are multiple ThisWorkbooks, the code never runs.
Actually, testing shows if I put it into ThisWorkbook1, it runs from there. LOL.
Main Question: Is there an event to create an Application Events object that doesn't use ThisWorkbook when opening a spreadsheet?
Basically another "code gate" that is always guaranteed to run that doesn't require ThisWorkbook.
I suspect "No", but any confirmation or alternative would be helpful.
Thanks.
The Workbook.Open event is the modern way to get code to run on open.
The legacy way is to have a specially named macro in a standard module:
Public Sub Auto_Open()
MsgBox "Works!"
End Sub
That should pop the message box on open.
As mentioned in the comments, a sane Excel file only has a single Workbook module - there being more than one means the file is corrupted in some way, and that can't be good. I'd recommend rebuilding the broken files: you never know when a corrupted file will just outright crash Excel for no apparent reason.
Don't know why the corruption is happening, but it's happening enough across various spreadsheets to be worrisome.
Created code to test for a bad ThisWorkbook upon opening and not allow saving if ThisWorkbook is bad.
1 regular module (modAutoOpenAndClose) and 1 class module (classWBEvents).
Putting these 2 modules into all spreadsheets as standard procedure. As mentioned above in the comments, users will then follow the instructions from Excel Experts Tips to Fix Corrupt Spreadsheet.
Hopefully these are useful to anyone else experiencing/preventing corruption. May seem like overkill, but this has been a bedeviling issue.
Option Explicit
'modAutoOpenAndClose
Dim WorkbookEvents As classWBEvents
Function ErrorIsThisWorkBookBad() As Boolean
On Error GoTo ErrLabel
ErrorIsThisWorkBookBad = Not (ThisWorkbook.CodeName = "ThisWorkbook")
Exit Function
ErrLabel:
ErrorIsThisWorkBookBad = True
End Function
Private Sub Auto_Open()
If ErrorIsThisWorkBookBad Then
Call MsgBox("This Spreadsheet is Corrupt." + vbCrLf + vbCrLf + _
"Please Copy Tabs And Modules to New Spreadsheet." + vbCrLf + vbCrLf + _
"Saving will not be allowed.", vbCritical)
End If
Set WorkbookEvents = New classWBEvents
Set WorkbookEvents.WB = ActiveWorkbook
If Not WorkbookEvents.WB Is Nothing Then
WorkbookEvents.WB_Open
End If
End Sub
Private Sub Auto_Close()
If Not WorkbookEvents Is Nothing Then
If Not WorkbookEvents.WB Is Nothing Then
Set WorkbookEvents.WB = Nothing
End If
Set WorkbookEvents = Nothing
End If
End Sub
And
Option Explicit
'classWBEvents
Public WithEvents WB As Workbook
Private Sub WB_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
If ErrorIsThisWorkBookBad Then
Call MsgBox("This Spreadsheet is Corrupt." + vbCrLf + vbCrLf + _
"Please Copy Tabs And Modules to New Spreadsheet." + vbCrLf + vbCrLf + _
"Saving will not be allowed.", vbCritical)
Cancel = True
End If
End Sub
Public Sub WB_Open()
'Reserved for project specific code
End Sub

Error handling not working on run-time Error 9 in excel-vba

I want to apply the error handling mechanism in Excel VBA, I want to catch this "runtime error 9", but it's not working.
I am using this userform_initialize() method/sub over and over again, each time I don't want to open this "SAMPLE UPDATE FILE.xlsm" workbook instead, I want to check if it's already open. if yes, then switch to that window or open that workbook.
I have tried on error resume next statement as well but still, it breaks on switching to window "Windows("SAMPLE UPDATE FILE.xlsm "). Select"
Private Sub UserForm_Initialize()
Application.DisplayAlerts = False
On Error GoTo OPEN_WB_ERR
Windows("SAMPLE UPDATE FILE.xlsm").Select
UserForm1.ComboBox1.RowSource = ("'X:\SAMPLE UPDATE FILE.xlsm'!SEARCH")
Windows("PROFORMA_INVOICE.xlsm").Activate
On Error GoTo 0
Exit Sub
OPEN_WB_ERR:
Workbooks.Open Filename:="X:\SAMPLE UPDATE FILE.xlsm"
UserForm1.ComboBox1.RowSource = ("'X:\SAMPLE UPDATE FILE.xlsm'!SEARCH")
Windows("PROFORMA_INVOICE.xlsm").Activate
Resume Next
End Sub
any advice will be helpful...
Check your setting in the VB editor (Tools >> Options >> General tab >> Error Trapping) for how errors are handled - if you have "Break on all errors" selected then it will always break regardless of any error handling you have set. "Break in Class module" is a good option.
Try,
Private Sub UserForm_Initialize()
Dim path As String, Fn As String
Dim Wb As Workbook
Fn = "X:\SAMPLE UPDATE FILE.xlsm"
Set Wb = Workbooks.Open(Filename:=Fn)
UserForm1.ComboBox1.RowSource = "'" & Fn & "'" & "!SEARCH"
ThisWorkbook.Activate
End Sub
The Initialize event procedure runs when the form is first created, before it is shown. You should open your workbook before creating the form, not as part of that process. Try a procedure like the one below, to be installed in a standard code module.
Sub OpenUserForm()
Dim MyForm As UserForm1
' open your workbook here
Set MyForm = New UserForm1 ' this fires the Initialize event
UserForm1.Show
' the code below runs when MyForm is closed
Unload MyForm
Set MyForm = Nothing
End Sub
Note that a form by the name of UserForm1 must exist. I recommend to give it another, more descriptive name. If you do that whatever name you give is the one to use in the Dim statement declaring MyForm.
I use a WorkbookIsOpen function
Public function WorkbookIsOpen(byval strFile as string) as Boolean
Dim wbkCurr as excel.workbook
WorkbookIsOpen = false
For each wbkCurr in application.Workbooks
If wbkCurr.name = strfile then
WorkbookIsOpen = true
Exit for
Endif
Next wbkCurr
End function
Pass just the file name and extension ie myworkbook.xlsx
Then I just adjust my logic accordingly

After a re-open function how can i get it to kick off my userform again?

So this function will effectively restart my workbook however, it is not kicking of my userform that i have set to open on workbook open and im not sure why. I dont know if it is bypassing that function or what...
Private Sub CommandButton3_Click()
Dim sPath As String
Dim sName As String
sName = ThisWorkbook.Name
sPath = ThisWorkbook.Path
ThisWorkbook.Saved = True
Workbooks.Open Filename:=sPath & "\" & sName
''''at a minimum I need this userform to show and it wont when i run this funtion.
UserForm1.Show
End Sub
You can't open the same file that you already have active. So you need to close it first to open it once again. It breaks your code. so it won't work. You need to close it.
If you want to have your UserForm to pop up at start just put my code to Workbook_Open and it will be opend each time you start your file.
Private Sub CommandButton3_Click()
'Vbmodal ensures that user need to interact with userform or discard it to select cells etc.
UserForm1.Show vbmodal
End Sub

Resources