Edit: I've managed to solve the problem below by using Application.EnableEvents = False in Workbook A to stop the Workbook_Open() code from running in Workbook B but am still stumped by the problem if anybody care to explain?
I've got Workbook A opening, editing, saving and closing Workbook B. As Workbook B is used by multiple users, I made it switch to Read-Only mode upon opening and display a pop-up message. However, when running the code from Workbook A, I want to stop the message box from opening. I used Application.OnTime to change Workbook B to Read-Only upon opening and to cancel the pending code upon saving within 10 seconds.
Problem: The code works when I open Workbook B directly and save within 10 seconds but when running a code from Workbook A to open=>edit=>save=>close Workbook B, Workbook B will open by itself even after it is closed.
After doing much research and trial and error by trying to use Public variables and Referencing VBA project, I settled on Application.OnTime using the resource below:
http://www.cpearson.com/excel/OnTime.aspx
Below is a sample of my code.
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call StopTimer
End Sub
Private Sub Workbook_Open()
Call StartTimer
End Sub
Public RunWhen As Double
Public Const cRunWhat = "Change_File_Access"
Public Const cRunIntervalSeconds = 15
Public DatabaseWB As Workbook
Sub StartTimer()
RunWhen = Now + TimeSerial(0, 0, cRunIntervalSeconds)
Application.OnTime EarliestTime:=RunWhen, Procedure:=cRunWhat, _
Schedule:=True
End Sub
Sub StopTimer()
MsgBox RunWhen
On Error Resume Next
Application.OnTime EarliestTime:=RunWhen, Procedure:=cRunWhat, _
Schedule:=False
End Sub
Sub Change_File_Access()
If Not Workbooks("WorkbookB.xlsm").ReadOnly Then Workbooks("WorkbookB.xlsm").ChangeFileAccess xlReadOnly
MsgBox "This file is set as Read-Only"
End Sub
Some quick testing shows the timer seems to be running within the context of the Workbook that the macro resides in - not the Workbook that you executed it from. So, you need to do one of 2 things - either quit the Excel application completely (probably not convenient) or reset the timer from the Worksheet you're running the code from.
I.e., in WorkbookA:
Dim book As Workbook
Set book = Workbooks.Open("WorkbookB.xlsm")
book.Save
Application.Run book.Name & "!StopTimer"
book.Close
Related
This is my first time posting on this site, and would appreciate some help. I'm a member of the mrexcel forums, and have received a lot of help from those great people over there, but have not been able to find an answer to my question. At work, I've created a workbook that allows us to enter data into different worksheets via a userform. I'm trying to make this as user-friendly as I possibly can, to attempt to eliminate as many "user-errors" as possible. One issue I'm trying to avoid is a user forgetting to close the sheet when their shift is over, and locking everyone out because the workbook is read-only when another user has the file open. So, I have a code that will save and close the workbook after 40 minutes of inactivity. One issue this is causing (and it is a very minor issue, but one that has been brought up to me by one of my employees), is that every once in a while, coincidentally the user will be entering data into the userform right around that 40 minutes of inactivity, and the workbook will close before they had a chance to submit the data. I was wondering if anyone could help me modify my code so that when it reaches that 40 minutes of inactivity, before it saves and closes, it sees that the userform is open and cancels the action if it is. Here is the code that I've got. Thanks for any help I can get.
In Module 1:
Dim CloseTime As Date
Sub TimeSetting()
CloseTime = Now + TimeValue("00:40:00")
On Error Resume Next
Application.OnTime EarliestTime:=CloseTime, _
Procedure:="SavedAndClose", Schedule:=True
End Sub
Sub TimeStop()
On Error Resume Next
Application.OnTime EarliestTime:=CloseTime, _
Procedure:="SavedAndClose", Schedule:=False
End Sub
Sub SavedAndClose()
ActiveWorkbook.Close Savechanges:=True
End Sub
In ThisWorkbook:
Private Sub Workbook_Open()
Call TimeSetting
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call TimeStop
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Call TimeStop
Call TimeSetting
End Sub
Just call the TimeStop() sub on the Userform_Initialize() event, and then call the TimeSetting() sub when you close the userform
You can see if a userform is loaded by using this function:
Function IsUFLoaded(ByVal UFName As String) As Boolean
Dim UForm As Object
For Each UForm In VBA.UserForms
If UForm.Name = UFName Then
IsUFLoaded = True
Exit For
End If
Next
End Function
And then in SavedAndClose Sub:
Sub SavedAndClose()
' change userform_name to your userform name
If IsUFLoaded("userform_name") = False Then ActiveWorkbook.Close Savechanges:=True
End Sub
Why not, instead of doing what you're suggesting: allowing a user to accidentally leave themselves logged in to a userform which would cause the issue you're trying to avoid, you instead also reset the timer with Call TimeStop and Call TimeSetting within the change events of fields in the userform?
I'm writing vba which manipulates data within a worksheet however I'm trying to make it run whenever a workbook is opened.
The problem I'm having is that due to the workbook (that the code needs to run on) is different/new every time, I need the auto_open code to be within a personal macro workbook.
Sub Auto_Open()
Dim bookname As String
Dim checkbook As String
Dim Workbook As Workbook
For Each Workbook In Application.Workbooks
bookname = Workbook.Name
checkbook = Left(bookname, 3)
If checkbook = "EDN" Then
Data_generator
Application.DisplayAlerts = False
ThisWorkbook.Save
Application.DisplayAlerts = True
Application.Quit
Else
End If
Next Workbook
End Sub
When this code runs it checks all open workbooks and sees if the first 3 letters of it are 'EDN', if it is then run the public sub called 'Data_generator', save it and quit. If it isn't check the next open workbook, etc.
When a file is opened from windows explorer, excel launches (with both the desired workbook and the personal macro workbook) however because excel opens the personal macro workbook first and runs the code before opening the desired workbook it doesn't find a workbook called 'EDN'.
If the above code is ran after both workbooks have opened then the code works as intended and cycles through each open workbook to see if there's one called 'EDN' (this was proved by putting a messagebox after the 'then' and running the code), if so run the sub.
I've proved this by putting a messagebox after the 'else', when this is done it displays the messagebox with the workbook I want, not open. After the message box is cleared, the workbook then opens.
Is there any way to make the desired workbook open first or any other work around for this?
You can create an Add-in that runs whenever a workbook is open
https://msdn.microsoft.com/en-us/library/office/gg597509(v=office.14).aspx
this tool may help to create the Add in http://www.andypope.info/vba/ribboneditor.htm
You should be able to use the Application.OnWindow event to trigger a macro when a file is opened or closed.
In ThisWorkbook
Private Sub Workbook_Open()
Call StartTracking
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call StopTracking
End Sub
In a Module
Function StartTracking()
Application.OnWindow = "AutoRunOnWindowChange"
End Function
Function StopTracking()
Application.OnWindow = ""
End Function
Function AutoRunOnWindowChange()
If Left(ActiveWorkbook.Name, 3) = "EDN" Then
Call Data_generator
Application.DisplayAlerts = False
ThisWorkbook.Save
Application.DisplayAlerts = True
Application.Quit
End If
End Function
I have an automated task that will open Excel and run some VBA code at a specific time every morning. If a user has the excel file open I want to warn them the file will be automatically closed in a few moments via a popup message, then shortly thereafter have Excel automatically save and quit. In case the user is away from their computer, I want the popup message to close automatically after 5 seconds so it doesn't freeze my SaveExit subroutine.
The popup message is appearing, but it will not close automatically.
It is only closing if "OK" is clicked. This is preventing the SaveExit routine from running.
Under Microsoft Excel Objects in This Workbook:
Private Sub Workbook_Open()
ThisWorkbook.Worksheets("Pull Data").Activate
Application.OnTime TimeValue("1:00:00 AM"), "CloseWarn", TimeValue("1:02:00 AM")
Application.OnTime TimeValue("1:03:00 AM"), "SaveExit", TimeValue("1:04:00 AM")
End Sub
In a module called autoclose:
Sub SaveExit()
Application.DisplayAlerts = False
With ThisWorkbook
.Save
Application.Quit
End With
End Sub
Sub CloseWarn()
Select Case CreateObject("WScript.Shell").PopUp("closing soon", 5, "Warning")
Case 1, -1
End Select
End Sub
How do I get a popup warning message to display at a specific time, then close if no user input occurs??
Figured it out! Thanks #Evan for pointing me in the right direction. I don't understand why, but when using the Application.OnTime command under Workbook_Open, things work better if I identify the module where the routine is located. After I figured this out I moved my subs to ThisWorkbook to streamline my code. I created a userform called CloseAlert that informs users that Excel is about to close automatically. The userform closes after 5 seconds, or upon user clicking "OK".
Under Microsoft Excel Objects in ThisWorkbook:
Private Sub Workbook_Open()
ThisWorkbook.Worksheets("Pull Data").Activate
Application.OnTime TimeValue("16:08:00"), "ThisWorkbook.ShowCloseAlert"
Application.OnTime TimeValue("16:09:00"), "ThisWorkbook.SaveExit"
End Sub
Private Sub ShowCloseAlert()
CloseAlert.Show vbModeless
Application.OnTime Now + TimeValue("00:00:05"), "Unloadit"
End Sub
Sub SaveExit()
Application.DisplayAlerts = False
With ThisWorkbook
.Save
Application.Quit
End With
End Sub
In a separate module called unload_CloseAlert:
Public Sub Unloadit()
unload CloseAlert
End Sub
The Goal: I'm trying to auto close out of workbooks for the reports I update as there are users who have them open, and forget to close them at night. I've set the auto close time to 12am.
Question 1: So I already have a couple of solutions for this problem. I found answers through a smattering of searches on google, and combined a few pieces of code to get exactly what I wanted. Now I'm wondering if it's possible to have the excel vba auto save & close in just one Module or ThisWorkbook. Essentially keeping all the code in one place.
Question 2: I know that the 1st and 2nd macro work I'm wondering which is more efficient and clean, and why?
1ST MACRO
' Insert into ThisWorkbook
Private Sub Workbook_Open()
Static SchedSave
If SchedSave <> 0 Then
Application.OnTime SchedSave, "SaveWork", , False
End If
SchedSave = TimeValue("09:29:00") ' Insert Desired time in Military
Application.OnTime SchedSave, "SaveWork", , True
End Sub
' Insert into module
Sub SaveWork()
ThisWorkbook.Save
ThisWorkbook.Close
End Sub
2ND MACRO
' Insert into ThisWorkbook
Private Sub Workbook_Open()
Reset
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Reset
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Reset
End Sub
' Insert into Module
Sub Reset()
Static SchedSave
If SchedSave <> 0 Then
Application.OnTime SchedSave, "SaveWork", , False
End If
SchedSave = TimeValue("12:00:00") ' 12AM
Application.OnTime SchedSave, "SaveWork", , True
End Sub
Sub SaveWork()
ThisWorkbook.Save
ThisWorkbook.Close
End Sub
Also posted: http://www.mrexcel.com/forum/excel-q...ml#post3830083 (This is where I got help to get the 1st Macro to work)
I might not be a genius but nothing prevents you to put the "module-code" in "thisworkbook".
I don't see why you have to call the "Reset" Macro each time the workbook is opened, each time a worksheet is changed and... well isn't a worksheet activated as soon as you open a workbook? I'd say the 1st is the best.
EDIT: Lol I didn't see the unformatted 'insert this into thisworkbook
I want to run a VBA macro AFTER the workbook has finished opening. I tried to use workbook_open but this runs before the workbook has finished opening. This doesn't work for me as I need to loop through each sheet like so...
Private Sub Workbook_Open()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
'do stuff on each sheet
Next ws
End Sub
Does anyone know if there is an event which runs once the workbook has finished opening? Or have any other suggestions on how I can achieve this?
I had a similar problem that I solved using Application.OnTime.
From the linked MSDN Library Article:
Schedules a procedure to be run at a specified time in the future
(either at a specific time of day or after a specific amount of time
has passed).
You could try using this method to provide the workbook with enough time to open before running the DoStuff procedure:
Private Sub Workbook_Open()
Application.OnTime Now + TimeValue("00:00:01"), "DoStuff"
End Sub
Ensure the DoStuff procedure is not in a worksheet module:
Private Sub DoStuff()
'Implementation...
End Sub
The time can be adjusted but one second was satisfactory for me.
Put your code in the Workbook_Activate event. It happens after the Open event.
Private Sub Workbook_Activate()
' Code goes here
End Sub
Try using ThisWorkbook rather than ActiveWorkbook:
Private Sub Workbook_Open()
Dim osht As Worksheet
For Each osht In ThisWorkbook.Worksheets
Debug.Print osht.Name
Next osht
End Sub