Excel option to calculate values for each cell only once - vba

I have a project whose requirements include a circular reference (just so you know why I'm deliberately putting this in my workbook).
Within the doc, I'd like for each cell in the circle to calculate only once. I know this is possible through the Windows Button > Excel Options > Formulas > Enable Iterative Calculations, but I have no way to enforce that option on everyone who touches the workbook. Is there any way (through VBA, maybe, or cell/sheet options?) to make a cell calculate its value only once?
Sounds like a longshot to me, but I really really appreciate your help :)

Something like this:
Private Sub Workbook_Open()
With Application
.Iteration = True
.MaxIterations = 1 '<-- or whatever the maximum allowable # you want
.MaxChange = 0.001 '<-- modify as necessary, or remove if desired
End With
End Sub
Private Sub Workbook_Close()
Application.Iteration = False
End Sub
Place these in the Workbook module.
This was one of the first answers on Google, which you would've found had you done any searching on your own:
http://www.mrexcel.com/forum/excel-questions/444155-need-macro-enable-iterative-calculation.html

Related

Apply autofilter using macro after worksheet has calculated?

So I've set up this spreadsheet at work - I end up having to do a lot of fiddly Excel tasks, though I barely know any VBA - and I want a table to automatically filter itself after the worksheet has been edited. The problem is that the column which is being filtered is full of formulas, which change in response to edits made by the user, and the filter gets applied before the column has finished calculating, producing erratic results. So really what I want is to apply AutoFilter after the worksheet has been calculated, rather than after it has been edited.
The macro I have been using is:
Private Sub Worksheet_Change(ByVal Target As Range)
With ActiveWorkbook.Worksheets("Library").ListObjects("Library")
.AutoFilter.ApplyFilter
End With
End Sub
Naively changing the activating event to Worksheet_Calculate() doesn't work - it appears to be repeatedly applying the filter over and over again.
I'm sure this is a relatively simple one and I just don't know what it is I need to do. Can you guys advise?
(PS first time posting here - hope I have done everything right!)
Never mind, have solved it myself. For anyone else with the problem, I just set calculation to manual and then replaced my code with:
Private Sub Worksheet_Change(ByVal Target As Range)
Calculate
ActiveWorkbook.Worksheets("Library").ListObjects("Library").AutoFilter.ApplyFilter
End Sub

VBA macro not triggering when target cell changes via form control option buttons

I literally just got my feet wet with VBA as this is my first macro. After many hours of searching, I couldn't seem to find an answer that had a solution that worked for me so here I am.
On Sheet3 I have 3 option buttons in a group box that are linked to cell "B18" on Sheet4 (Sheet4 is hidden to the user, a backstage if you will). When any of the three option buttons are selected, 'Sheet4!B18' gets updated as it should (e.g. 1, 2, or 3). What I want to happen is to have 'Sheet3!B17' changed based upon the value in 'Sheet4!B18', or effectively: IF('Sheet4!B18'=2,SET('Sheet3!B17'="Some Text Here:"),SET('Sheet3!B17'="0%")), but still allow user input in 'Sheet3!B17'. I have one VBA macro on Sheet4 with the following code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Worksheet.Range("B18") = 2 Then
Worksheets("Sheet3").Range("B17") = "Some Text Here:"
Else
Worksheets("Sheet3").Range("B17") = "0%"
End If
End Sub
If I manually update 'Sheet4!B18' then the macro gets triggered with the desired results. If I use any of the 3 option buttons on Sheet3, the macro does not get triggered even though the target cell is getting updated.
In my searching I couldn't seem to find anything concrete, but from what I could tell the "Worksheet_Change" function doesn't see changes to cells from form control as changes to the linked cell are considered a "recalculation" as if it were from a formula. I don't know how correct that is, but my searching led me to believe that I would need another macro assigned on the 3 buttons and/or group box that when either of those get selected/changed, it would somehow trigger the working macro on Sheet4.
I thought that perhaps I could create a new macro that I would assign to the group box or option buttons themselves so I tried that and could not get anything to work. I tried adding the above macro code to another sub, Sub mode() and assigning to only the group box, then only the buttons, but nothing happened in either case. I proceeded to try tweaking the code just in case the references were not correct, but saw no change regardless of how I specified the reference. I am not getting any error messages, but nothing gets triggered unless I manually change the value in 'Sheet4!B18'.
Is there a way to get the first macro that I have working on Sheet4 to trigger off of the option buttons changing the target cell value, something like forcing it to look only at that one specific cell for changes? Am I stuck making another macro for the buttons and/or group box to trigger the macro on Sheet4? Am I over-complicating this and there is some built in Excel sheets function(s) that I can use?
IF/THEN is a fine way to do it. VBA also supports ternary logic with the IIF function, like this:
Worksheets("Sheet3").Range("B17") = IIF(Worksheets("Sheet4").Range("B18") = 2, "Some Text Here:", "0%")
That may seem a little difficult to read, but it's a good concept to understand, since it's present in many languages, and usually with a more simplified implementation that makes it very useful and concise.
Also, I would suggest making a couple of other alterations that may make your code easier to write, read and maintain (especially as it becomes more complex).
First, alias the worksheets, something like this:
Dim this as Worksheet: Set this = Worksheets("Sheet3")
Dim that as Worksheet: Set that = Worksheets("Sheet4")
Now you would be able to rewrite your code like this:
If that.Range("B18") = 2 Then
this.Range("B17") = "Some Text Here:"
Else
this.Range("B17") = "0%"
End If
And the ternary approach would now be:
this.Range("B17") = IIF(that.Range("B18") = 2, "Some Text Here:", "0%")
And you can get as specific as you like with the aliases. For instance, you could realias the ranges, instead of just the worksheets, like this:
Dim this as range: Set this = Worksheets("Sheet3").Range("B17")
Dim that as range: Set that = Worksheets("Sheet4").Range("B18")
this = IIf(that = 2, "Some Text Here:", "0%")
Also, I find it easier to use the cells property than the range property, especially when you start having to do cell math. In that case, Range("B17") becomes Cells(17, 2).
You can also change the way the cells are referenced in the spreadsheet by typing Application.ReferenceStyle = xlR1C1 into the immediate window. That way you don't have to mentally convert between A2 style ranges to Cartesian style (2,1).
Sometimes you just have to go through your entire thought process and type everything out before you have an "ah-hah!" moment because that is exactly what I had happen. I said to myself, "Why can't I have just one macro that gets triggered by the option buttons that checks my linked cell then proceeds to update the cell I want?" Well, eventually I was able to find the right code and this is what worked perfectly:
Sub mode() ' mode is the name of this macro
If Worksheets("Sheet4").Range("B18") = 2 Then
Worksheets("Sheet3").Range("B17") = "Some Text Here:"
Else
Worksheets("Sheet3").Range("B17") = "0%"
End If
End Sub
As it turns out, I was overlooking the simple solution and the above macro is all I need once I assigned it to the 3 option buttons in my group box, but not the group box itself. Since users will not have access to the hidden Sheet4 and therefore 'Sheet4!B18' will never have manual user input, the macro I first had on Sheet4 could be removed safely. Due to the fact that the option buttons being chosen is the trigger for the assigned macro, it executes each time the option is changed and only when the option is changed. Perfect!
EDIT:
Thanks to Chris Strickland for some tips for better code! I went on to modify the above into what you see below for slightly better performance (using Cells() instead of Range()), to save the original value to another cell and restore it if option 1 or 3 were selected, used aliases, and finally the IIf operator.
Sub mode() ' mode is the name of this macro
Dim S3 As Worksheet: Set S3 = Worksheets("Sheet3")
Dim S4 As Worksheet: Set S4 = Worksheets("Sheet4")
If IsNumeric(Cells(17, 2)) = True Then
S3.Activate
S4.Cells(18, 3) = Cells(17, 2).Value
End If
S3.Cells(17, 2) = IIf(S4.Cells(18, 2) = 2, "Some Text Here:", S4.Cells(18, 3))
End Sub

Making excel graphs appear/disappear

I want a graph only to appear when a condition is fulfilled. To be more precise: I have a drop down menu that changes the content of a graph. If the menu point "Total revenue" is clicked, I want a second graph to appear. I am new to VBA and this is what I came up with so far:
Sub Iffuntion()
Dim SelectedChart As Range
Dim notVisible As Boolean
If Range("D100").Value = Range("E100").Value Then
ActiveSheet.ChartObjects("Testchart").Visible = True
Else
ActiveSheet.ChartObjects("Testchart").Visible = notVisible
End If
End Sub
It works, but I have to execute the VBA to make the graph appear/disappear and I would like that to happen automatically. Also the condition should eventually be in another worksheet to keep the sheet with the graphs nice and tidy. I read that to achieve this I have toI have to activate the other worksheet. Would you recommend this way or is there a better solution?
Thanks for the help and best regards!
Pete
EDIT: Here is the link to a sample file with the proposed solution of Cor_Blimey, that I couldn't get to work properly. The interconnections in the excel are more complicated than they would have to be, but I wanted to be as accurate ad possible in displaying what is actually happening in my excel. Thanks for taking a look!
https://dl.dropboxusercontent.com/u/18406645/sample.xlsm
Assuming you mean that they change, from a data validation drop down list, the contents of a cell then you can put your code into the Worksheet's Worksheet_Change event. This event is fired when the user changes the content of a cell (or by an external link).
Then test for the cell being the right cell then run your code.
Like:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Intersect(Target, Me.Range("D100"))
If Not rng Is Nothing Then
If rng.Value = Me.Range("E100").Value Then
Me.ChartObjects("Testchart").Visible = True
Else
Me.ChartObjects("Testchart").Visible = False
End If
End If
End Sub

Call a function when only a specific Excel cell changes on Formula Recalculation

As far as i know, Worksheet_Calculate is called when the value of any cell in a worksheet changes on Formula recalculation.
Is there a way so that i need a function to be called only when a specific cell changes on Formula Recalculation
To make something happen when a specific cell is changed, you need to embed the relevant selection change event within the file Worksheet_Change(byval target as Range). We can re-calculate a worksheet when your cell changes as follows:
Private Sub Worksheet_Change(byval target as range)
If target.address = Range("YourCell").Address Then Application.Calculate
End Sub
Now what you want to do is switch off calculations the rest of the time. If you only want to switch off calculations on the single sheet (and not your whole file), you will need to turn calculations off when it is activated, and on when deactivated e.g.
Private Sub Worksheet_Activate
Application.Calculation = xlCalculationManual
End Sub
Private Sub Worksheet_Deactivate
Application.Calculation = xlCalculationAutomatic
End Sub
Of course, your requirements to re-calculate may be considerably more complex than the example above. Firstly, you may open the file whilst on the sheet in question in which case you should use the Workbook_Open event to detect your sheet, and set calculations accordingly
Then you may have several cells that may require some sort of calculation. Presumably the reason you want to switch off calculations is that the file is running too slowly. If so, and you are identifying all the input cells, you could enter the outputs using code. One method would be to enter the formulas using this guide to entering formulas in Excel Visual Basic. You could then replace the formula with the calculated value e.g. Range("YourCell") = Range("YourCell").Value...so stopping the number of formulas in the sheet constantly growing
Let me see if I interpret your question correctly. You want to know if it is possible to only kickoff a macro if a particular cell (or group of cells) is changed.
The answer is Yes. To tweak Ed's code a little.
Private Sub Worksheet_Change(byval target as range)
If Not Intersect(target.address, Range("YourCells")) is Nothing Then
MyMacro()
End If
End Sub
I think your use of "function" is throwing people off. That's why Ed's answer is so elaborate.
Ok. It is possible that you stated your question correctly and you just want to gain efficiency. In that case, Ed's answer solves your immediate problem, but will cause the spreadsheet NOT to calculate automatically when you change other cells.

Run VBA macro when condition is met

I'm creating a spreadsheet to train my numerical skills.
Now, I use VBA macros to generate a new problem once the current one has been correctly solved. To do so, I still have to press a button in the worksheet, which costs time and is annoying.
Is there a way that I can execute a macro when a certain condition is met?
for instance:
if A1 = "correct!"
then <run macro>
else <do nothing>
Also, let me know if you're interested in the spreadsheet, I'd be happy to share it with you guys.
best,
Pieter
Add this as code for your Worksheet:
Private Sub Worksheet_Change(ByVal Target As Range)
If (Range("A1") = "correct!") Then
''# do your stuff here
End If
End Sub
Worksheet_Change is called whenever something is changed. As it looks like A1 is calculated, you can not check for Target in that case but check for the value of the cell.