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

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.

Related

Is there a way using sheet protection to allow a user to enter values but protect formats?

I have a worksheet containing a column of numbers. The column is formatted with a background color, number formats, etc. The column is unlocked. I protect the sheet manually by right-clicking on the tab and selecting Protect. In the Protect Sheet dialog, 'format cells' is unchecked. I interpret to mean that the user should not be able to format cells. Yet when the user pastes into the column, formats are pasted along with values.
If I protect the sheet in VBA using
sh.Protect UserInterfaceOnly:=True
I get the same result: formats are pasted. I do not specify AllowFormattingCells:=False because the default is False.
I have seen posts suggesting that formats can be restored by copying and pasting them from a shadow area. I have used this solution before I started protecting worksheets and found it overly complex. I had hoped this was something protection would handle. If there is a way to handle it, I'd like to do it in VBA.
This question is a little old, but I had the same one, and so have many people in the past. With a bit of browsing I came up with a solution which seems quite clean and appears to work. Am I missing something?
Private Sub Worksheet_Change(ByVal rngTarget As Range)
Dim vPaste As Variant
With Application.CommandBars("Standard").Controls("&Undo")
If Not .Enabled Then Exit Sub
If .ListCount < 1 Then Exit Sub
If .List(1) <> "Paste" Then Exit Sub
End With
vPaste = rngTarget.Value2
On Error Resume Next
Application.EnableEvents = False
Application.Undo
rngTarget.Value2 = vPaste
Application.EnableEvents = True
On Error GoTo 0
End Sub
This could go in Workbook_SheetChange, but not if you already have code in Worksheet_Change, because the worksheet event handler gets called before the workbook event handler. But this code can go in a module to keep things tidy.
There is no built-in protection option you can use to achieve your desired result.
The only thing that works in this case is the clunky workaround that you mention, i.e. use a Worksheet_Change event that ensures the correct format after a cell has been modified.
Since there are many different ways to paste content, i.e. via various menus, ribbon commands, keyboard shortcuts, etc., any VBA solution that tries to intercept pasting will become very complex, much more complex than the change event that restores the format to its original state.
Another option might be user education and training (so they know to paste values only), although user behaviour may be the toughest element to change in the whole scenario.

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.

Managing Excel Worksheet_Change Feature

I have created a spreadsheet which uses the Worksheet_Change feature and the code associated with that works very well. I can stop it firing unnecessarily when inside the module by using Application.EnableEvents = False.
However, while I've created a form to enter data directly into the next available row (again, that works fine in terms of entry) it doesn't cause the formulae in the sheet to calculate (even though auto calculation is on and re-enabled within the module). If I manually place my cursor in the row, hit F2 and simply press enter, everything then fires up.
I have tried to enter data directly into the cells, but of course the Worksheet_Change feature then kicks in again and the cursor isn't simply moving to the next adjacent cell ....
I've tried to check firs for any direct entry with the code below and if it looks like the user isn't entering directly into the cell, the Worksheet_Change is disabled:
Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo eventhandler
Sheets(1).Range("a1").Select
LastCell2 = Sheets(1).Cells(Rows.Count, "A").End(xlUp).Row
Dim intersection As Range
Set intersection = Intersect(Target, Range("A3:F" & LastCell2))
If intersection.Row = LastCell + 1 Then
Exit Sub
End If
Application.EnableEvents = False
The code above is simply checking to see if data is being entered into the next empty cell and if that's the case I want it to just exit there but it isn't working.
So I actually have 2 problems :
the first is why this formula isn't triggering after entry via a vba form - I've used INDIRECT since there are other macros that delete rows by moving the remaining cells up and that was causing the count in the $A$3:$A$500 to reduce to $A$499 and then 498 etc - the addition is done depending on the system date and the transaction date so I get a current value and a future value using a standard sum statement:
=AD1-(SUMIF(INDIRECT("$A$3:$A$500"),"<="&TODAY(),INDIRECT("$E$3:$E$500")))+(SUMIF(INDIRECT("$A$3:$A$500"),"<="&TODAY(),INDIRECT("$F$3:$F$500")))
The second is why I can't enter data directly into the spreadsheet and trap the fact that I don't want it to do anything and simply allow the user to hit enter and move to the next adjacent cell to the one they just entered data into.
Is this a lost cause and am I trying to do too much here? I'm relatively new to coding and teaching myself so apologies if the standard and style isn't to everyone's taste.
Thanks in advance for any replies.

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!

How do I determine whether calculation was completed, or detect interrupted calculation?

I have a rather large workbook that takes a really long time to calculate. It used to be quite a challenge to get it to calculate all the way, since Excel is so eager to silently abort calculation if you so much as look at it.
To help alleviate the problem, I created some VBA code to initiate the the calculation, which is initiated by a form, and the result is that it is not quite as easy to interrupt the calculation process, but it is still possible. (I can easily do this by clicking the close X on the form, but I imagine there are other ways)
Rather than taking more steps to try and make it harder to interrupt calculation, I'd like to have the code detect whether calculation is complete, so it can notify the user rather than just blindly forging on into the rest of the steps in my code. So far, I can't find any way to do that.
I've seen references to Application.CalculationState, but the value is xlDone after I interrupt calculation, even if I interrupt the calculation after a few seconds (it normally takes around an hour).
I can't think of a way to do this by checking the value of cells, since I don't know which one is calculated last. I see that there is a way to mark cells as "dirty" but I haven't been able to find a way to check the dirtiness of a cell. And I don't know if that's even the right path to take, since I'd likely have to check every cell in every sheet.
The act of interrupting calculation does not raise an error, so my ON ERROR doesn't get triggered.
Is there anything I'm missing? Any ideas?
Any ideas?
I think the trick you need to implement (if you're application runs in Excel 2007 or later) is to handle this with the Application.AfterCalculate event, which is raised after both calculation is complete and there are no outstanding queries.
If you've never worked with events in VBA before, there is a good overview from cpearson.com.
The (MSDN) solution by Charles Williams above worked for me where I had 1000's of VLOOKUP's that neeeded to recalculate as the code was changing the lookup value because of an iteration loop. Results were skewed as calculations were not running to 100% completion.
At the beginning of my subroutine the code executes
Application.Calculation = xlManual
This eliminated unnecessary calculations by Excel until I was ready.
Now at the critical point the code executes
Application.Calculation = xlAutomatic
ThisWorkbook.ForceFullCalculation = True
Application.Calculate
Having forced Excel to perform a full calculation, the code could then saved the result and move onto the next iteration ... but before doing so
ThisWorkbook.ForceFullCalculation = False
Application.Calculation = xlManual
Remembering at the very end
Application.Calculation = xlAutomatic
I've never actually used it but I think this might work to prevent calculation from being interrupted.
Application.CalculationInterruptKey = xlNoKey
I think I'm hearing that you need a way to monitor whether each step within the calculations being performed was executed.
Assuming that you're not interested in re-engineering the workbook to use methods that are easier to track than spreadsheet calculations (such as volatile calculations within VBA or Pivot Tables), this may work for you:
Within VB, you can utilize .EnableCalculation and .Calculate to set an entire worksheet as "Dirty" (needing calculation) and then recalculate. The key difference between this and your current process is that we will perform these actions one worksheet at a time in manual mode. By initiating the calculations one worksheet at a time from within VBA, you will be able to perform additional intermediate actions that can be used to track how far you got in the calculation process.
Please note that this approach assumes a fairly linear workbook structure such that your workbook will produce the correct results if we first recalculate Sheet1, then Sheet2, Sheet3, and so on, in whatever order you wish. If your formula dependencies are more "spaghetti" than linear, this probably won't work for you. It also assumes you are working in Excel 2000 or later.
For example, you could write a VBA routine that accomplishes the following steps.
You will need to know your dependencies in order to know which calculations must come before others, and start with the worksheet in a "clean" state where no calculations are currently pending.
Step 1: Set the active sheet to the first worksheet where recalculation is needed
Step 2: Set the calculation mode to manual as follows:
Application.Calculation = xlCalculationManual
Step 3: "Dirty" the entire active sheet as follows:
With ActiveSheet
.EnableCalculation = False
.EnableCalculation = True
Step 4: Initiate a recalculation for this worksheet only (not the entire workbook) using:
.Calculate
End With
Note that if the calculation mode were set to automatic, Step 3 would initiate a re-calculation across the entire workbook. By using manual mode and With, we are constraining that calculation to the current sheet.
Now you have dirtied and re-calculated the first sheet (hurray!). Now, by embedding Steps 3 and 4 above into a For/Each or For/Next loop, you can repeat the process for each worksheet in your workbook. Again, make sure you know the order in which your worksheets need to be calculated (if an order is needed).
Now for the big finish - by creating a counter variable within your loop, you can track how far you got in the calculations by updating your counter variable value each time you complete a worksheet calculation. For example, after you recalculate a worksheet, you can set the counter value to current value + 1 and store the results either in a global variable (so that it will persist even after your VBA routine ends), or in a cell within your worksheet. That way, you can check this value later to see how many worksheets were updated before the calculations finished or were interrupted.
If you have relatively few worksheets in your workbooks, the same approach could be applied to one range at a time rather than a sheet.
I won't go into detail about how to construct a "counter", loops, or global variables here, but if needed, this information can be easily found using your favorite search engine. I would also highly recommend re-enabling automatic calculations once you are done as it is easy to forget that it's been set to manual mode.
I hope this works for you - for more information on calculation modes and recalculation, this is a helpful link:
http://msdn.microsoft.com/en-us/library/bb687891.aspx
Perhaps the following would work:
Do Until Application.CalculationState = xlDone
DoEvents
Loop
Can't say I've tested it, nor that I know how robust the functionality of Application.CalculationState really is to determine whether 'complete' calculation occurred, as opposed to something interrupting the process and flagging the calculation state as done.
Private sub SomeCodeThatGeneratesFormulas
Application.Calculation = xlCalculation.xlCalculationManual
'...Some formulas are copied here'
Application.OnTime DateTime.DateAdd ("s",.01,DateTime.Now), "Module1.CalculateFullRebuildAndSubsequentSteps" 'By using Application.OnTime, this method will be called in a way that locks the end-user out of providing inputs into Excel until the calculation itself is complete.
end sub
public sub CalculateFullRebuildAndSubsequentSteps
Application.CalculateFullRebuild
'...Do next steps, i.e. paste as values'
end sub
On the status bar, right hand side, it will say Calculating (N processors) X% (where N is the number of processors on your computer and X% is how much it has completed) when recalculating. If you don't see text there, it's not recalculating.
I'm using Office 2010, but it should be there in all versions. It's just kinda subtle so it's easy to miss.
Arrays in Excel can be a bit stupid. That is that in order to accomplish some tasks people avoid to use intermediate columns/rows to store (temporary) data, so arrays have to recalculate staff from the beginning every time, thus getting really slow. My Suggestion would be:
fix arrays to avoid multiple searches. Use hidden cells or even hidden sheets
Avoid using A:A and rather use A1:A1000 specially in excel 2007 or later
use formulas to equal zero or error (ex: NA()) while previous items aren't calculated, so you can clearly see if an operation is done at all.
some VBA could be used to inject formulas in place one step at a time, perform calculations, then proceed to next step, but this could mean lots of work...

Resources