简体   繁体   中英

Volatile User-Defined Function not recalculating as expected (VBA/Excel)

It seems Application.Volatile and ActiveSheet.Calculate are not what I thought they were, I need help creating a Function that recalculates correctly when relevant data changes. Here's what I have going right now:

 Public Function Availability(EmpName As Range)
 Application.Volatile (True)
 ColLetter = Cletter(EmpName.Column)
 Set NameRange1 = Range("$" & ColLetter & "$1:$" & ColLetter & "$" & (Application.ActiveCell.Row - 1))
 Set SumRange1 = Range(Cletter(Application.ActiveCell.Column) & "1:" & Cletter(Application.ActiveCell.Column) & (Application.ActiveCell.Row - 1))
 Sum1 = Application.SumIfs(SumRange1, NameRange1, EmpName)
 Availability = Sum1
 End Function

So as you can see, it takes a cell and does a sumifs using that cell to provide the criteria and the criteria column, using the column the function is placed in for the actual numbers to sum (I know this seems like reinventing the wheel but I'm just posing the most simple form of my problem). It performs these calculations on every previous row, essentially.

CLetter is just another small function that turns a Column# into the letter because I'm too lazy to invoke R1C1 and find this more readable.

But whenever I make changes to the criteria or sum ranges, instead of recalculating to account for changes in the identifiers or the numbers, it seems to forget that the affected line exists (even if I just changed it to MATCH the criteria) or just throw a #VALUE error. This is easily fixed by clicking on the "=Availability(A#)" function and hitting enter, but I would hate to have write an extra macro to refresh all of these cells when they'll be all over the spreadsheet. Toggling Volatility helped a little bit but didn't solve the problem, nor did adding some variations on the following sheet change event:

 Private Sub Worksheet_Change(ByVal Target As Range)
 ActiveSheet.Calculate
 End Sub

Surely there is some way to get a function to refresh itself whenever ANY cell is altered, not just its own.

First make sure your calculations are set to Automatic in the options. If your calcs are set to automatic this will change every time the data to which it refers is changed.

Second part of the problem is you are referencing the active cell. If you change the data your active cell is the one that just changed and not the one the formula is in. Use Application.Caller which refers to the cell from which the Function is called.

Third use Cells instead of calling another function to find the column letter. Cells allows for number references.

Public Function Availability(EmpName As Range)
Application.Volatile

Set NameRange1 = Range(Cells(1, EmpName.Column), Cells(Application.Caller.Row - 1, EmpName.Column))
Set SumRange1 = Range(Cells(1, Application.Caller.Column), Cells(Application.Caller.Row - 1, Application.Caller.Column))
Sum1 = Application.SumIfs(SumRange1, NameRange1, EmpName)
Availability = Sum1
End Function

IMHO it is a mistake to use Application.Volatile as a bandaid for ignoring Excel's recalculation rules
- why not do it properly and pass ALL the ranges that that the UDF needs to the UDF as parameters?
Then you won't need application volatile etc and your workbook and UDFs will calculate much more cleanly.

Since calculation was already automatic, and the whole point was not to add more arguments to be defined (ie to have it all be automatic except the criterion for sumif) the only necessary change was making Application.ActiveCell into Appplication.ThisCell. Application.Caller may also have worked but this way seemed more certain. Thanks for your help Tim and Scott!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM