Why is xlCalculationAutomatic making my macro hang? - excel

I've created a macro in Excel for Office 365 to copy data from a source workbook (Source) into an array and append that array to the end of of table in an analysis workbook (Analysis). The Analysis workbook is fairly formula-heavy, so can take a minute to update when changes are made.
At the beginning of the macro, I use Application.Calculation = xlCalculationManual to speed things up and at the end I use Application.Calculation = xlCalculationAutomatic to return them to normal.
Excel hung when I tried to run the macro today. It got stuck at the point that it showed "Calculating (4 threads): 50%" until after several minutes I force-closed it. It did this twice, and upon further investigation -- as expected -- I found that it was occurring at the last line in the macro: Application.Calculation = xlCalculationAutomatic.
I commented out that line and ran the macro again with no issues, then used the F9 key to calculate the sheet and it calculated very quickly.
So my question is: why is the macro hanging when I use xlCalculationAutomatic in the code if Excel has no problem doing a full calculation after the macro has run? Is there a way around this?
Here are the last few lines of the code:
' Paste data; copy and paste formatting
rngPaste.Value = WorksheetFunction.Transpose(arrTemp)
rngTblOldLastRow.Copy
rngPaste.PasteSpecial xlFormats
NormalMode:
Application.ScreenUpdating = True
'Application.Calculation = xlCalculationAutomatic
End Sub

The entry xlCalculationAutomatic makes Excel calculate the result of every individual formula, from the moment that any change to the sheet is made.
E.g. you have an Excel file, containing thousands of formulas, and you have a macro, which modifies or adds hundreds of entries. When you have the mode xlCalculationAutomatic, then all thousands of formulas will be executed at every of the hundreds of entries your macro is modifying or adding. Excel does not hang at such a moment, but it might take a lot of time to finish the task.
Without that xlCalculationAutomatic, your macro will pause the calculation of the formulas, add or modify your hundreds of entries and only calculate all formulas once.
Therefore it is advised not to use this mode while you are working with macros, except, of course, when your macro is using formula results.
A more professional explanation can be found here.

Related

How stop PivotTable re-calculation after I change measure via VBA

Using VBA macros, I have to change measure formulas in data model in Excel.
The code snipped which changes formula of every measure:
ActiveWorkbook.Model.ModelMeasures(1).Formula = textOfNewMeasureFormula
Right after the macros changes every particular measure, Excel starts re-calulating the PivotTable. In case of big data it may take a long time and so inconvenient. It would be great to change all the measure needed and launch re-calculation after all that changes.
But I cannot find a solution for that.
This code doesn't work, because it's about updating data:
activeSheet.PivotTables(1).ManualUpdate = true
It doesn't have any effect on the calculation after formula measure change.
This doesn't help either, because it's about formulas in Excel Sheets:
Application.Calculation = xlCalculationManual
Just in case - I didn't check
Application.Visible = false
because it is anyway not acceptable for my case.
Thanks a lot in advance.
In the settings (File->Options->Formula), there is an option for formula calculation. If you change it to "manual", it will disable automatic calculations. However, you need to control the update manually, you can use "F9" button for it.
If you want to control this setting in macro, you can use:
Application.Calculation = xlManual
to turn calulation method to manual
Application.Calculation = xlAutomatic
to turn the calculation method to automatic.

How to make Autofill more efficient, or an alternative to Autofill

I'm currently working on a document that needs to autofill a selection of cells down an area that is changing, although it will regularly be upwards of 25000 or more rows. The cells that I am attempting to autofill downwards are filled with "complex" formulas, that are configured to work with autofill.
When I have more than 15000 rows, to run one instance of
Worksheets("sheet1").Range("A1:A4").AutoFill Destination:=Range("A1:A" & LastRow), Type:=xlFillDefault
this will take upwards of 20 minutes to completely execute this one line, so if I need to run this 5 times for an example, we're looking an estimated 100 minutes of run time for 5 lines of code.
I'm curious if there's a more efficient way to either use AutoFill, or execute the task I'm looking to achieve.
I haven't really found much that was helpful, I did change my range a few times to see if that increases or reduces times.
If I change LastRow to be equal to say, 50-1000 this line of code runs instantaneously. Once we creep above the 1000 rows, this starts to run incredibly slow.
Ideally, If I can find an additional way to run this that would significantly decrease my run times, that would be great.
There are some boilerplate code I use frequently to squeeze a better performance out of my VBA code:
Sub Test()
Application.EnableEvents = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'Code here
....
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Application.ScreenUpdating = False makes your spreadsheet not jump around. Can't provide you with hard data here, but from my personal experience updating the screen slows down your macro quite significantly.
Application.Calculation controls how calculation is handled, and is defaulted to xlCalculationAutomatic. Under this default setting, all formula field gets updated when there is a change in value of a referenced cell. Setting it to xlCalculationManual before running your VBA, then back to xlCalculationAutomatic will ensure that calculations are only performed one time, instead of throughout your script when numbers are changing all the time.
Application.EnableEvents controls codes that is triggered by event (like saving, or activating a worksheet). Just in case, I set it to False.
Remember to set everything back to their default values at the end of your vba, or under your ErrorHandler section of your VBA.
Just FYI, having these two application settings changed makes it a harder to debug your code if you rely on looking at your spreadsheet to debug. Best to completely remove them during debugging, and add them back once you are ready to push to production.
Without your specific formula, it is hard to further optimize. There is one other way you may try that MAY speed things up.
Sub MyAutoFill()
My_Formula = "SUM(1,3)" 'Replace this with your complex formula, remember to escape your inverted commas
Worksheets("sheet1").Range("A1:A" & LastRow) = "=" & My_Formula
End Sub
The above code sets the values of a range to a string the begins with "=", which becomes an excel formula on the worksheet. This MAY be more efficient than autofill, try it out and report back!

Inserting Row in Excel Takes a long time

I have an excel document (10 sheets). 1 sheet has at this point about 1600 rows and about 148 columns of information. There are no formula's in this sheet.
At first I thought it was a problem with my Macro. But it turns out manually inserting a row (or deleting) gives the same problem: a 14 second delay (for ONE ROW!). I've tried deleting all conditional formatting, Data Validation, (deleting hidden graphs that get there data from a different sheet). I've tried turning of Auto Calculation (which I also do in the Macro), I've tried inserting/deleting a row in "Design Mode"> But nothing seems to help.
One version of my file (I have different back-ups) seems to have fixed it. But I cannot reproduce this in my current file. I don't know what the fix was.
I've scoured the internet for solutions but have yet to find one. I'm running on excel 2010. Who knows the trick I need? Or is updating to excel 2016 my best option?
cheers!
The three primary causes of recalculation lag are calculation, event handling (due to a Worksheet_Change) and (to a lesser degree) screen updating.
Put this 'helper' sub procedure in a public module code sheet.
Public Sub appTGGL(Optional bTGGL As Boolean = True)
With Application
.ScreenUpdating = bTGGL
.EnableEvents = bTGGL
.DisplayAlerts = bTGGL
.AutoRecover.Enabled = bTGGL 'no interruptions with an auto-save
.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
.CutCopyMode = False
.StatusBar = vbNullString
End With
Debug.Print Timer
End Sub
Use it like this,
sub main()
appTGGL btggl:=false
'do everything you have to do here
appTGGL
end sub
You can add or remove application environment settings as you see fit.
If the problem turns out to be a badly written Worksheet_Change then post it here or on code review for improvement,
quick update: somehow the problem is fixed when I remove to other sheets. These sheets are not connected by formula references in any ways. Both sheets have (flat) information implanted by macro's, but after the macro is done it's just flat information. I've tried disabling al relevant macro's but this didn't solve the issue. Only deleting the other sheets: If i deleted 1 of the sheets the time would go from 14 sec to 7 seconds, and if I deleted the other as well it would go to 0 seconds.
It seems as though somehow these sheets are connected by some kind of calculation (outside of the macros) but I cannot imagine what this might be. Any Ideas?

Excel VBA - Selecting multiple slicer items at once without a refresh

I'm trying to select and deselect multiple slicer items from a single slicer without having it trigger an update until all my selections are complete. I'm able to do this pretty simply in the Excel front-end by Ctrl-clicking all the selections I want. However, when I record a macro with this behavior and run it, it updates after each selection/deselection (the recorded macro is just a bunch of .Selection = True/False statements within a With block).
I've tried using the SlicerCaches.VisibleSlicerItemsList function, but that throws a 1004 Application error - even when I've used the SlicerItem.Name field to populate the array:
Dim tntw(0 To 2) as Variant
For i = 0 To 2
tntw(i) = sc.SlicerItems(i + 1).Name
Next i
sc.VisibleSlicerItemsList = tntw
I've also tried setting all dependent PivotTables to manual update for this, as well as trying to set the application.calculation to manual (and switching both back at the end), but neither accomplish what I'm looking for.
Any ideas?
Before your selection put
Application.Calculation = xlmanual
After your selection
Application.Calculation = xlautomatic
This worked perfectly for me. I had the exact same problem.
As #joseph4tw posted in the comments, all that is really needed is the Application.EnableEvents = False line. However, I needed to re-enable events again before the final Slicer was iterated to make the event actually fire. I don't have access to the code any longer (previous job) but imagine the solution involved counting the number of Slicers and at n-1 in the loop call to re-enable events.

Is there any effect if I do not restore the application.calculation in vba?

I have turned off the
application.calculation = xlcalculationmanual
and after such a lengthy vba code I have done these at the end
application.calculation = xlcalculationautomatic
What I saw is these statement is taking 20 seconds and sometimes hanging up when I restore the xlcalculation
application.calculation = xlcalculationautomatic
I really do not understand why it is taking a lot of time for that statement. To save the time, I just neglected to restore it. Is there any effect if I do not restore it back?
The consequence is that no calculations will be done. So if you have a cell with formula =A1+A2 and you change the values of A1 and A2, then the result won't be updated to the actual sum of the current values of A1 and A2 until you force a calculation manually F9 or select automatic calculation again. This can also be done manually in Tools > Options... > Calculation.
What I suspect is happening is that it it re-calculating the enture workbook.
Ideally, you should not disable automatic calculations because this is dangerous. (i.e. you may be looking at old cached values instead of recent values)
Instead you should verify that the problem is recalculation (i.e. manually recalculate the sheet and see how long it takes). If that is the problem, you should google way of speeding up calculations (i.e. splitting up your data, using static references, etc...)

Resources