I want to take an action in an Excel workbook macro after a period of inactivity (hide/protect some worksheets). What is the best/simplest way to achieve this?
Í'm assuming I'll use Application.OnTime to periodically check if the user has been active. But what events should I handle to see if the user was "active" (i.e. has does something - anything - with the workbook)?
Clarification: I want to detect all activity, not just changes. I.e. including mouse clicks, selecting, copying, navigating with the keyboard, changing worksheets, ...
I'm assuming that when a UI event happens that represents user activity, I will set a variable thus:
LastActivityTime = Now
and the macro run by Application.OnTime will check this variable to see if the user has been active recently. Which events (other than SheetChange) would I need to handle to set this variable? I had kind of hoped there would be KeyUp and MouseUp events, these two would probably have been enough.
Update: I have implemented this by handling Workbook_SheetActivate, Workbook_SheetSelectionChange and Workbook_WindowActivate. Realistically this is probably enough.
I have implemented this by handling Workbook_SheetActivate, Workbook_SheetSelectionChange and Workbook_WindowActivate. Realistically this is probably enough.
I can only see two solutions -- either handle evary single event the Application object has or use GetLastInputInfo function.
One simple way is to compare the content of the workbook with that of the last time you check. I believe combining this with Application.OnTime will solve your concern.
Related
I have a long running application written in VBA. Because it runs for a long time I call a sub from the main loop every iteration to check if the user has pressed the escape key. The sub is listed below. The code works great except that it is always listening, even when the VBA application does not have the focus. What's the best way to approach this? Is there an alternative to GetAsyncKeyState which only listens when the correct application is in focus, or are there system calls I can use to check that the correct window is in focus.
Private Sub checkForUserEscKeyAbort()
'Listen for escape key and exit gracefully if user aborts.
Dim abortResult As VbMsgBoxResult
If GetAsyncKeyState(vbKeyEscape) Then
abortResult = MsgBox("Escape key pressed, do you want to abort?", vbYesNo)
If abortResult = vbYes Then Call teardownSLICER
End If
End Sub
The problem is the way GetAsyncKeyState operates. It does more than just check it the key is currently down, it also checks if the key was pressed since the last time GetAsyncKeyState was called. Thus, your problem of "always listening".
You could use GetKeyState, but frankly, I'm not a fan of that option either. Your code must be well crafted to avoid a polling window that is so small that you literally have to hold the key down to ensure it isn't missed.
A viable alternative is key masking where you use a combination of keys such as Shift-Escape. There is a decent introduction to this (and many other subjects) on Chip Pearson's site. However, this is still not my preferred method.
My preference has already mentioned in the comments. Your application may best be improved with a user form. It also gives you the ability to get information in front of the user. Perhaps they wouldn't try quitting the application if a progress bar on the user form indicated 95% completion. Maybe you can add a pause button that free's some resources for a prescient need and then resumes when the user is ready. That little extra functionality on its own is enough to win me over but there is an even better reason for using a User Form - it does exactly what you asked!
User forms, and many user form controls, have Keydown, Keyup, and Keypress events that only trigger when the form (or control) have focus. This is precisely what you wanted.
I've been wanting to write a macro to automatically save my powerpoint file every 5 minutes. Can anyone help?
I know there's an auto-save built-in but that's only good for auto recover. I'm attempting to have this ppt being saved every 5 mins so that other users using it at the same time will see the updates come in (using Office 365).
Thanks!
I don't think you'll be able to do this with a simple macro, but you might be able to make it work with an add-in.
The add-in would:
Trap events, specifically the SelectionChanged event.
Every time the event fires, the event handling code compares the current time to the time it last fired (stored in a static variable).
If more than x minutes have elapsed since the event last fired, saves the presentation and resets the last-fired time.
If the event never fires during a session, it means that nothing changed, so no real reason to save.
A further refinement: before or after checking the time difference, check the presentation's .Saved property. If True, then nothing has changed in the presentation, so again, no reason to save.
I could be wrong, but most answers I've seen regarding saving in intervals use the OnTime method. This isn't actually available in PowerPoint, so I'm not actually sure that what you're wanting to do is possible.
There are a half-dozen answers to this. "Open a second instance" "Have a pause" Etc. I'm not looking for that.
I'm looking for the user of the workbook to be able to manipulate the workbook while the macro is running. I've seen this working before, where the user could scroll around, change tabs, even add and remove data, all while the macro was running. Unfortunately, I couldn't get permission to look at the code (And committing CFAA violations ins't my cup of tea), so I have no idea how they did it.
How can you enable a user to edit the workbook as macros are running? For a specific example, I have Conway's Game of Life running. Users select cells to flip live/dead, then can run a macro to run the entire thing. I think it'd be nice for users to be able to change cells as the macro is running. (which is a second on select macro)
Thank you
Sorry just reread the question. I wouldn't expect the permutation to run for very long - not long enough to interrupt really.
But if it does, then the advice about using lots of DoEvents stands.
The other option is that you can use the OnTime event to have a "heartbeat"
VBA Macro On Timer style to run code every set number of seconds, i.e. 120 seconds
You can set the timer to say 3 seconds. Every time the OnTime event occurs you do one step of your permutation. In the three seconds in between they can edit.
Refactor your macro to use Events. In which case, you would have a series of event handlers (instead of one monolithic macro) to respond to various triggers. This is assuming that the macro is influenced by what the user is doing in the worksheet.
One way of (sort of) doing this is to use a Modeless Userform (UserForm.Show vbModeless)
The user form stays visible but the VBA stops running when the form is shown and the user can then interact with Excel. Then when the user clicks a button on the form the code behind the button starts running again.
So in reality the user is either interacting with Excel or interacting with the form ...
I am trying to set permissions for my form fields (continuous form) and permissions need to be re-evaluated every time a different record gains focus/selection. Right now the only way I've found is to have an OnFocus event for each editable control, but there's gotta be a better way...
I've already tried MouseMove, OnClick, etc. but they don't seem to work when clicking/moving from a control in one record to the next without clicking over empty space first. Also MouseMove seems to have a limit to how frequently it responds.
I would also appreciate something equivalent to an "On Focus Changed" event, if "On Record Changed" is not possible.
Try OnCurrent, or maybe OnDirty, and can also try OnAfterUpdate.
I usually put stops in all of the candidate events when I'm not sure which one to use. There are differences, but running your application and seeing when the events fire can help you decide which one to use.
The event you are looking for is : Form_Current
I have an Excel sheet that updates every 1 Second (via Labview using ActiveX)and has to be visible to the user.
And I cannot set "Application.UserInteractive" to "FALSE" throughout the application as the User could use the system for other excel operations.
And I have the same problem with "WorkSheet.UserInterfaceOnly"
The problem that occurs is:
If the user has selected a range of data ("mousedown" and has not released it) and at the same time the software tries to write the data .... boop it doesn't work...
I can set it to wait until user release the mouse click.... But is there a way to prevent this problem by not allowing to detect mouseclick events or block it some other way
Well Anywho...
I found a simple solution....
Protect the worksheet and set EnableSelection to noSelection