Don't execute Worksheet_Calculate event on Workbook_Open - excel

I have an excel workbook.
In one of the worksheets I make use of the Worksheet_Calculate event. This works fine. However when the workbook is first opened I do not want MyFunction to be called. What is the best away to do this?
My only idea so far is the following (I don't like this though). On the workbook open method put a time stamp in one of the worksheets and then have an if statement in my worksheet_calculate and if the current time is 1 minute past the time stamp (that was created on the workbook_open event) run the code otherwise don't.
Thinking there must be a better way though?
Private Sub Worksheet_Calculate()
MyFunction()
End Sub
Update
The reason I do not want my code to execute when the workbook opens is because there are some Bloomberg formulas that take a little time to execute so initially some of the cell values are #NA.
This causes a type mismatch error - any errors that happen are logged and an e-mail is automatically sent. So every time the workbook is opened there is an 'error' as the bloomberg formulas have not updated straight away

The code below should work for you.
The first time calculate is called it sets the flag to allow future calls to process. This means the first call to calculate does not process your code.
ThisWorkbook Code:
Private Sub Workbook_Open()
bFunctionFlag = False
End Sub
Sheet Code:
Public bFunctionFlag As Boolean
Private Sub Worksheet_Calculate()
If bFunctionFlag = True Then Call MyFunction
bFunctionFlag = True
End Sub
Private Function MyFunction()
MsgBox "Calculate"
End Function

In the workbook open event you can have
Private Sub Workbook_Open()
Application.EnableEvents = False
'Run Workbook Open code
Application.EnableEvents = True
End Sub
or if you need events to run from external services then do something similar with a public variable. Eg Dim wbOpenEventEnded as Boolean

Related

Select cell in worksheet_open, ONLY the first time it is activated

I was looking for a script that selects a cell like ("A5") to reset the view of the sheet, but only the first time the sheet is activated.
I have a basic Worksheet_Activate, but this resets me every time I move between sheets and back.
Thanks in advance!
Private Sub Worksheet_Activate()
Range("A5").Select
End Sub
Assuming you mean, "In a multisheet workbook, whenever I select/activate a certain sheet, I want to control (reset) the view of that sheet by selecting a certain cell, but I only want that to happen the first time I select/activate that sheet, during the time the workbook is open."
Public variables persist while the workbook is open, maybe this:
Public isInitialized As Boolean
Private Sub Worksheet_Activate()
If Not isInitialized Then
'put your first time only code here, ie Range("A5").Select
isInitialized = True
End If
End Sub
The Public variable will be false until the first time the designated sheet activates, then will persist True while the workbook is open.
Note that the Worksheet_Activate event doesn't fire between open workbooks, in that case, use the Workbook_Activate event in ThisWorkbook.

Excel VBA ActiveX combobox change event infinite loop

Let me preface by saying I am a self taught novice at VBA or coding for that matter.
I have a combobox "cmbStyle" not in a userform but diretly on a worksheet named "Cost". I need to run code on the change event of "cmbStyle". The problem I have is that when the user changes the combobox the event fires and the code runs but at the end of the code the combobox event fires again and so on and so on.
I know that Application.EnableEvents = False will have not effect on ActiveX controls so this is not a solution.
I have found descriptions on how to use a boolean variable to stop the looping in the change event for listboxes but I can't seem to get it to work in my instance.
My code will end the subroutine after the Cange Event fires the second time. However, the next time the user selects another value from the Combobox the CodeDoneRun variable is still TRUE so the subroutines won't run again when I need it to.
I feel I am missing something very basic here....
My code is as follows:
Public CodeDoneRun as Boolean
Private Sub cmbStyle_Change()
If CodeDoneRun = True Then Exit Sub
CodeDoneRun = True
Call other Subroutines
End Sub
Enclose your function call in the if statement and let the code run through.
Public CodeDoneRun as Boolean
Private Sub cmbStyle_Change()
If Not CodeDoneRun Then
Call other Subroutines
End If
CodeDoneRun = Not CodeDoneRun
End Sub

Delay Macro Run, Excessive Countifs

I am running a macro that opens a file referencing the one I am working in, pastes the relevant items as values into a separate sheet and makes a workbook out of that sheet.
The reason why I am doing this is because there are several thousand countifs, averageifs, and processor-intensive ilk.
The program runs from start to finish, just fine. The issue is that only a few of the items are calculated before the copy/paste operation and so I get a lot of #VALUE errors on the copy of the sheet with the formulas--even though the formulas are calculating correctly on further inspection.
I suspect the correct course of action is to delay the run until the sheet finishes calculating. Any and all help would be appreciated.
EDIT: I've tried all manner of application.calculations and nothing seems to be working. The links and items calculate normally if I open manually and let the processor do its thing. The only items that calculate are the ones that contain "COUNTA" somewhere in it. Is it possible that the application calculation methods don't work with Countifs and the like?
Shouldn't be that hard to do - the Worksheet object has a Calculate property that fires after it calculates. You can add a custom property to the worksheet that exposes a flag that you set after it is done calculating. In the worksheet code that has the time consuming calculation...
Option Explicit
Private can_copy As Boolean
Public Property Get CopyOK()
CopyOK = can_copy
End Property
Private Sub Worksheet_Calculate()
can_copy = True
End Sub
Private Sub Worksheet_Activate()
can_copy = False
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
can_copy = False
End Sub
'For volitile functions.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
can_copy = False
End Sub
...and in the calling code:
Dim book As Workbook
Set book = Application.Workbooks.Open("C:\foobar.xlsm")
Do While Not book.Worksheets("Sheet1").CopyOK
DoEvents
Loop
'Do your thing...
Note that I likely missed some events that would trigger a recalculation, but this should cover the scenario of just opening it.
So, I found a means for this to work:
Do Until Application.CalculationState = xlDone
Application.Calculate
While Application.CalculationState <> xlDone
MsgBox Application.CalculationState
DoEvents
Wend
Loop
It was a solution I sort of applied from Siddharth Rout : Wait until Application.Calculate has finished
Thank you everyone for your help!

How to call a Excel VBA procedure through late binding

I'm looking for a bit of advice here.
I have a spreadsheet with various modules and procedures that can be called from a Worksheet_Change event. This causes problems when I need to issue a sheet from the workbook for other users to complete.
Whenever the user tries to update the sheet, the on change event gets triggered, causing a compile error as the procedure being called does not exist, and this cannot be trapped (as far as I'm aware). I've tried using Application.EnableEvents = False, but this is in the worksheet event and the code breaks as soon as the event is triggered.
Is there anyway to call a procedure through late binding where I can trap the error?
I'm trying something like this at the moment.
Dim mdl as object
' Test for module in workbook, if error, then exit routine
Set mdl = Application.ActiveWorkbook.VBProject.VBComponents("mdlSharedFunctions")
'If no error, then call procedure here
call mdl.UpdateData(Target)
'Or
Application.Run mdl.UpdateData(Target)
Neither of these call methods will work and I'm hoping someone out there will be able to point me in the right direction.
Cheers
Pete
You can use a global variable as a flag - bit dirty but it works fine. Then add an If flag = true then statement to the change event sub.
Public globalflag as Boolean
Sub test1()
If globalflag = True Then
BrokenSub 'This sub has an invalid sub/function referenced, but will be ignored if the flag is set to false
Else
'Don't run the code
Exit Sub
End If
End Sub
Sub BrokenSub()
invalidfunction ("asb")
End Sub
EDIT
To put it in a worksheet, just see if the variable exists:
Declare this in a module in your master spreadsheet:
Public globalflag as Boolean
Then in your worksheet code
If not IsEmpty(globalflag) Then
BrokenSub 'Put your master spreadsheet code here - it'll run if globalflag exists and be ignored if it doesn't
End If
Sub BrokenSub()
invalidfunction ("asb")
End Sub

VBA to prevent a change event from triggering if another change event triggers

this one may be impossible to solve in VBA but I'd like to see what you experts have to say about it.
I have a textbox on a userform that triggers a macro within a TextBox1_Change() type of sub.
If the user types "ABC" in the textbox, the macro gets triggered 3 times: once for "A", once for "AB" and once for "ABC". This macro is actually kind of heavy, so I would like it to run only when the user is actually done typing, and not inbetween single key strokes.
I know I can make the user "press enter" or whatever and only then run the macro, but this is not what I'm looking for. I want him to type freely and see the results of his typing dynamically show up, with no other type of interaction required.
So, I came up with the idea of making the change event wait and see if another change event gets triggered within, say, 1 second from the first. If that happens, the first change event aborts.
Now this would work, and I think I would know how to code it, except that I don't know how to give the user the power to keep typing even when the first change event is running.
What I mean is that when the first macro runs, it "freezes" everything. Waiting to see if another change event triggers will therefore not work, as nothing is going to trigger until the first macro is done running.
Do you guys see my problem here? How would you go about this? Any chance I can achieve the results I'd like?
Any help is greatly appreciated, thanks guys!
I tested the following, and it works (assuming I correctly understand what you're trying to do).
In a code module, write this:
Public aRunIsScheduled As Boolean
Public nextRunTime As Variant
Sub MyMacro()
'Flag macro as having been run, no longer scheduled.
aRunIsScheduled = False
'Place your macro code here.
'I'll just use some dummy code:
MsgBox "MyMacro is running!"
End Sub
In your sheet module:
Private Sub CommandButton1_Click()
If aRunIsScheduled Then
' Cancel the previously scheduled run.
Application.OnTime EarliestTime:=nextRunTime, _
Procedure:="MyMacro", Schedule:=False
aRunIsScheduled = False
End If
' Schedule a new run 3 seconds from now:
nextRunTime = Now + TimeValue("00:00:03")
Application.OnTime EarliestTime:=nextRunTime, _
Procedure:="MyMacro", Schedule:=True
aRunIsScheduled = True
End Sub
I put a Commandbutton in my sheet and here I'm using its change event, but you can put this code in your TextBox1_Change() event instead, in exactly the same way.
reference: http://www.cpearson.com/excel/SuppressChangeInForms.htm
To suppress events in a form, you can create a variable at the form's module level called "EnableEvents" and set that to False before changing a property that will cause an event to be raised.
Public EnableEvents As Boolean
Private Sub UserForm_Initialize()
Me.EnableEvents = True
End Sub
Sub Something()
Me.EnableEvents = False
' some code that would cause an event to run
Me.EnableEvents = True
End Sub
Then, all of the controls on form should have a test if that variable as their order of business in any event code. For example,
Private Sub ListBox1_Change()
If Me.EnableEvents = False Then
Exit Sub
End If
MsgBox "List Box Change"
End Sub
You can declare the EnableEvents as Private if only procedures with that form need to suppress events. However, if you have forms that are programmatically linked together, such UserForm2 adding an item to a ListBox on UserForm1, you should declare the variable as Public and set it for another form with code like the following:
UserForm1.EnableEvents = False
'
' change something on UserForm1
'
UserForm1.EnableEvents = True
The primary difference between the EnableEvents property and code shown above and the Application.EnableEvents property is that with a UserForm EnableEvents, all control on the form must have code to exit if EnableEvents is True. In other words, all the form's controls must cooperate and respect the setting of EnableEvents.

Resources