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!
Related
How can I automatically execute an Excel macro each time a value in a particular cell changes?
Right now, my working code is:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("H5")) Is Nothing Then Macro
End Sub
where "H5" is the particular cell being monitored and Macro is the name of the macro.
Is there a better way?
Your code looks pretty good.
Be careful, however, for your call to Range("H5") is a shortcut command to Application.Range("H5"), which is equivalent to Application.ActiveSheet.Range("H5"). This could be fine, if the only changes are user-changes -- which is the most typical -- but it is possible for the worksheet's cell values to change when it is not the active sheet via programmatic changes, e.g. VBA.
With this in mind, I would utilize Target.Worksheet.Range("H5"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("H5")) Is Nothing Then Macro
End Sub
Or you can use Me.Range("H5"), if the event handler is on the code page for the worksheet in question (it usually is):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then Macro
End Sub
I spent a lot of time researching this and learning how it all works, after really messing up the event triggers. Since there was so much scattered info I decided to share what I have found to work all in one place, step by step as follows:
1) Open VBA Editor, under VBA Project (YourWorkBookName.xlsm) open Microsoft Excel Object and select the Sheet to which the change event will pertain.
2) The default code view is "General." From the drop-down list at the top middle, select "Worksheet."
3) Private Sub Worksheet_SelectionChange is already there as it should be, leave it alone. Copy/Paste Mike Rosenblum's code from above and change the .Range reference to the cell for which you are watching for a change (B3, in my case). Do not place your Macro yet, however (I removed the word "Macro" after "Then"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
End Sub
or from the drop-down list at the top left, select "Change" and in the space between Private Sub and End Sub, paste If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
4) On the line after "Then" turn off events so that when you call your macro, it does not trigger events and try to run this Worksheet_Change again in a never ending cycle that crashes Excel and/or otherwise messes everything up:
Application.EnableEvents = False
5) Call your macro
Call YourMacroName
6) Turn events back on so the next change (and any/all other events) trigger:
Application.EnableEvents = True
7) End the If block and the Sub:
End If
End Sub
The entire code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B3")) Is Nothing Then
Application.EnableEvents = False
Call UpdateAndViewOnly
Application.EnableEvents = True
End If
End Sub
This takes turning events on/off out of the Modules which creates problems and simply lets the change trigger, turns off events, runs your macro and turns events back on.
Handle the Worksheet_Change event or the Workbook_SheetChange event.
The event handlers take an argument "Target As Range", so you can check if the range that's changing includes the cell you're interested in.
I prefer this way, not using a cell but a range
Dim cell_to_test As Range, cells_changed As Range
Set cells_changed = Target(1, 1)
Set cell_to_test = Range( RANGE_OF_CELLS_TO_DETECT )
If Not Intersect(cells_changed, cell_to_test) Is Nothing Then
Macro
End If
I have a cell which is linked to online stock database and updated frequently. I want to trigger a macro whenever the cell value is updated.
I believe this is similar to cell value change by a program or any external data update but above examples somehow do not work for me. I think the problem is because excel internal events are not triggered, but thats my guess.
I did the following,
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheets("Symbols").Range("$C$3")) Is Nothing Then
'Run Macro
End Sub
How can I automatically execute an Excel macro each time a value in a particular cell changes?
Right now, my working code is:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("H5")) Is Nothing Then Macro
End Sub
where "H5" is the particular cell being monitored and Macro is the name of the macro.
Is there a better way?
Your code looks pretty good.
Be careful, however, for your call to Range("H5") is a shortcut command to Application.Range("H5"), which is equivalent to Application.ActiveSheet.Range("H5"). This could be fine, if the only changes are user-changes -- which is the most typical -- but it is possible for the worksheet's cell values to change when it is not the active sheet via programmatic changes, e.g. VBA.
With this in mind, I would utilize Target.Worksheet.Range("H5"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("H5")) Is Nothing Then Macro
End Sub
Or you can use Me.Range("H5"), if the event handler is on the code page for the worksheet in question (it usually is):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then Macro
End Sub
I spent a lot of time researching this and learning how it all works, after really messing up the event triggers. Since there was so much scattered info I decided to share what I have found to work all in one place, step by step as follows:
1) Open VBA Editor, under VBA Project (YourWorkBookName.xlsm) open Microsoft Excel Object and select the Sheet to which the change event will pertain.
2) The default code view is "General." From the drop-down list at the top middle, select "Worksheet."
3) Private Sub Worksheet_SelectionChange is already there as it should be, leave it alone. Copy/Paste Mike Rosenblum's code from above and change the .Range reference to the cell for which you are watching for a change (B3, in my case). Do not place your Macro yet, however (I removed the word "Macro" after "Then"):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
End Sub
or from the drop-down list at the top left, select "Change" and in the space between Private Sub and End Sub, paste If Not Intersect(Target, Me.Range("H5")) Is Nothing Then
4) On the line after "Then" turn off events so that when you call your macro, it does not trigger events and try to run this Worksheet_Change again in a never ending cycle that crashes Excel and/or otherwise messes everything up:
Application.EnableEvents = False
5) Call your macro
Call YourMacroName
6) Turn events back on so the next change (and any/all other events) trigger:
Application.EnableEvents = True
7) End the If block and the Sub:
End If
End Sub
The entire code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B3")) Is Nothing Then
Application.EnableEvents = False
Call UpdateAndViewOnly
Application.EnableEvents = True
End If
End Sub
This takes turning events on/off out of the Modules which creates problems and simply lets the change trigger, turns off events, runs your macro and turns events back on.
Handle the Worksheet_Change event or the Workbook_SheetChange event.
The event handlers take an argument "Target As Range", so you can check if the range that's changing includes the cell you're interested in.
I prefer this way, not using a cell but a range
Dim cell_to_test As Range, cells_changed As Range
Set cells_changed = Target(1, 1)
Set cell_to_test = Range( RANGE_OF_CELLS_TO_DETECT )
If Not Intersect(cells_changed, cell_to_test) Is Nothing Then
Macro
End If
I have a cell which is linked to online stock database and updated frequently. I want to trigger a macro whenever the cell value is updated.
I believe this is similar to cell value change by a program or any external data update but above examples somehow do not work for me. I think the problem is because excel internal events are not triggered, but thats my guess.
I did the following,
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheets("Symbols").Range("$C$3")) Is Nothing Then
'Run Macro
End Sub
I am trying to activate a macro by clicking in a cell.
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Selection.Count = 1 Then
If Not Intersect(Target, Range("B37")) Is Nothing Then
Worksheets("DaysEditor").Activate
Sheets("DaysEditor").Columns("C:LY").Hidden = False
Sheets("DaysEditor").Columns("C:EX").Hidden = True
Sheets("DaysEditor").Range("A1").Select
End If
End If
End Sub
This code works on a cell that doesn't have a formula but will not work on cells with a formula in them.
A few minor tweaks I would change with your code. Firstly, if you are going to make multiple calls to a worksheet, I suggest that you either make a variable of it or use it within a With <obj> statement.
Worksheets() is essentially a function. Every time you use it it has to get the function's "value" - hence not the most efficient way of doing things.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Address = "$B$37:$C$37" Then
With Worksheets("DaysEditor")
.Columns("C:LY").Hidden = False
.Columns("C:EX").Hidden = True
.Activate
.Range("A1").Select
End With
End If
End Sub
Generally I advise against using the Activate method. But since you appear to actually want to be on the worksheet when the code runs then it's not a big deal here. Just remember that Activate is not necessary for the remainder of your code to function.
One last tweak is that I moved your Activate method after you hide/unhide the columns. Probably not a big deal either, but no need to watch the screen redraw unnecessarily.
Thanks to #K.Davis he helped me figure out that the problem was the that cell I was attaching this to was a merged cell and that was the problem. I changed the Selection.Count = 1 to Selection.Count = 2 and the code works perfectly!
I have simple code which is moving worksheet "Data" to after activated worksheet.
Sub Workbook_SheetActivate(ByVal Sh As Object)
Worksheets("Data").Move After:=Worksheets(Sh.Name)
Worksheets(Sh.Name).Activate
End Sub
I am using
Worksheets(Sh.Name).Activate
because without this line worksheet "Data" remains selected after the move which is not the intention.
The problem I have is when this code is run it takes about 2-3 seconds for excel to think about it before seeing result.
I don't understand why. Without chaining these 2 operations together it takes milliseconds. Could someone please explain how to improve this and why is this happening?
This should be quicker:
Sub Workbook_SheetActivate(ByVal Sh As Object)
Dim nam As String
nam = Sh.Name
Application.EnableEvents = False
Worksheets("Data").Move After:=Worksheets(nam)
Worksheets(nam).Activate
Application.EnableEvents = True
End Sub
This happens because of recursive calling: when you use .Activate, then your Sub Workbook_SheetActivate is called again and you're stuck in an endless loop.
If you simply want to deselect the data range, you can use Cells(1,1).Select and you can directly use Sh instead of using Worksheets(Sh.Name), since they're equivalent.
So your final code would be:
Sub Workbook_SheetActivate(ByVal Sh As Object)
Worksheets("Data").Move After:=Sh
Cells(1,1).Select
End Sub
Hope this helps.
Thanks for your input guys. Turning off/on events helped to get out of the loop.
Application.EnableEvents = False
Worksheets("Data").Move After:=Sh
sh.Activate
Application.EnableEvents = True
I should have probably explained what the code was being used for in my question.
The user had 50+ Worksheets in the Workbook (please don't ask me why :)). They had one in particular ("Data") they wanted to be able to click in and out whilst working with other worksheets. So this code was simply causing the Worksheet "Data" to "follow them"
The line:
sh.Activate
Was used to get back to the Worksheet they have just clicked on as otherwise they were getting stuck on "Data" Worksheet.
I have a workbook with multiple Worksheet_Change events in the sheet.
Each of these macroes are related to an active x combobox.
The problem is that when I change one of the comboboxes the macro fires (as expected), which in turn start another macro (which is not suppose to run). I have already set the Application.EnableEvents = False
But the issue might be that I am changing a cell.value, which is linked to another combobox and hence also linked to another worksheet_change event.
A workaround as I see it, might be to only run the macro, if the combobox is the one actually selected, but here comes the second problem. I can't find a way to have vba return the name of the active combobox.
Please note that these comboboxes is not connected to a userform, they are simply placed directly on the worksheet.
Is there anybody who has any idea on how to solve this??
Any help is much appreciated,
I see that there are 2 possible solutions...
1 - as stated by Gary's Student, you may have toggled the Application.EnableEvents somewhere unknowingly and you could try and trap where that happens.
2 - Maybe set a global boolean variable called, say DontRunMacros and set it to TRUE at the start of the first macro and to FALSE at the end. Then you simply have each other macro start with If DontRunMacros Then Exit Sub - That will prevent the others running regardless of the events that fire...
Hope that helps!
You could also set a global variable handling events. You check it at the beginning of each change event.
Dim ufEventsDisabled As Boolean
Sub YourSub()
ufEventsDisabled = False
Range("A1").Value=1 'This triggers the event
ufEventsDisabled = True
Range("A1").Value=1 'This doesn't trigger the event
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If ufEventsDisabled=True Then Goto ExitEvent:
'Your regular worksheet code
ExitEvent:
ufEventsDisabled=False
End Sub
There is probably an error somewhere that is re-Enabling Events...........You can always insert:
MsgBox Application.EnableEvents
at points in your code to trap this.
Thanks a lot for the quick responses.
I ended up using a named cell in the worksheet, similar to what hstay sugested.
If ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 0 Or Me.Name <> ActiveSheet.Name Then Exit Sub
However as I need the worksheet to calculate some cells while the code is running, I can't set application.Calculation = xlManual.
So the code still tries to execute a bunch of other change events, but the code above stops them from running more than just the first line. This however still takes a lot of time, which is quite frustrating. Guess I'll just need to take this in to account another time.
This is how I begin and end all my worksheet_chnage events:
If ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 0 Or Me.Name <>
ActiveSheet.Name Then Exit Sub
ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 0
Application.ScreenUpdating = False
Application.EnableEvents = False
'some random code that trigger other change_events
Application.EnableEvents = True
Application.ScreenUpdating = True
ThisWorkbook.Sheets("MD").Range("AllowMacro").Value = 1