16

I followed this guide to create the following custom function which counts the number of cells of a certain colour in my spreadsheet:

Function ColorFunction(rColor As Range, rRange As Range, Optional SUM As Boolean)
Dim rCell As Range
Dim lCol As Long
Dim vResult
lCol = rColor.Interior.ColorIndex
    If SUM = True Then
       For Each rCell In rRange
        If rCell.Interior.ColorIndex = lCol Then
                vResult = WorksheetFunction.SUM(rCell) + vResult
        End If
       Next rCell
    Else
        For Each rCell In rRange
        If rCell.Interior.ColorIndex = lCol Then
                vResult = 1 + vResult
        End If
       Next rCell
End If
ColorFunction = vResult
End Function

It works fine and gives me the expected result if I click the green tick in the formula bar but won't update automatically after I colour another cell. How do I set it to automatically update the count every time I manually colour a cell elsewhere on the spreadsheet?

Extra info: I'm using Excel on the Mac and I've checked in settings and automatically calculate is turned on.

7 Answers 7

18

Excel Recalculates UDFs Only When Their Input Changes

You've created a User-Defined Function (UDF). Excel only runs the code of a UDF when the cell(s) that serve as input to the function change.

For example, suppose I have this UDF:

Public Function MyFunction(Target As Range)
MsgBox "The target cell's address is " &  Target.Address
End Function

And in cell A1 on my worksheet I reference the cell with the formula:

=MyFunction(A2)

During normal use of my workbook, the code of my UDF will only be called if there's a change to the contents of cell A2 or another cell referenced by a formula in A2.

Solutions

You can work around this behavior in several ways:

  1. Force Excel to manual recalculate all of the formulas in the workbook even if they have not changed since the last calculation by pressing Ctrl+Alt+F9 on Windows and (I assume on Mac) Cmd+Alt+F9. If that doesn't work you can also try adding Shift which additionally rechecks dependent formulas before recalculating.
  2. Include a volatile function in one of the cells referenced by your UDF. Do this by modifying the definition of your VBA function to accept an additional parameter:

    Public Function MyFunction(Target As Range, Optional VolatileParameter As Variant)
    

    Then edit the cell referencing your UDF to pass the result of a volatile function such as Now() to the UDF:

    =MyFunction(A2,Now())
    

    The net result will be that Excel will deem the cell containing the reference to your UDF as needing to be recaculated every time the worksheet is changed because it references a volatile function.

  3. Edit the cell containing the UDF. Simply entering the cell then pressing Enter without making changes should be sufficient to trigger an update.

  4. Create macro button that runs the following line of code:

    Application.CalculateFull
    
  5. Place the following code in your Workbook's Open event:

    ActiveWorkbook.ForceFullCalculation = True
    

    This causes Excel to always recalculate all formulas, regardless of whether it believes recalculation is required. This setting remains in effect until Excel is restarted.


Why Doesn't Excel Always Recalculate My UDF?

When Excel performs an automatic recalculation, it does not recalculate every single formula in the workbook. Instead it only updates those cells which contain formulas that refer to the most recently modified cell. This method avoids the much lengthier process of needlessly performing many calculations across the entire workbook only to come up with the same result for the vast majority of them.

With a UDF, the only references in the workbook of which Excel's calculation engine is aware are those identified in the function's inputs. Excel cannot examine the VBA code within the UDF and identify other cells that may influence the function's output. So while the function's author may construct the function to return different results based on any number of changes made in the workbook, the only cells Excel knows will change the function's result are the declared inputs. Therefore, changes to these cells are the only ones Excel pays attention to when deciding whether the UDF needs to be recalculated.

2
  • 1
    You can mark an UDF as volatile too, by calling Application.Volatile. See my answer.
    – neclepsio
    Sep 16, 2019 at 14:07
  • Small typo: =MyFunction(A2;Now())
    – lalebarde
    Jan 24, 2020 at 18:11
6

An User Defined Function can be marked as volatile by itself, forcing the recalculation every time something changes in the workbook.

To do that, you have to call Application.Volatile from within the function.

Function ColorFunction(rColor As Range, rRange As Range, Optional SUM As Boolean)
Application.Volatile

Dim rCell As Range
Dim lCol As Long
Dim vResult
lCol = rColor.Interior.ColorIndex
    If SUM = True Then
       For Each rCell In rRange
        If rCell.Interior.ColorIndex = lCol Then
                vResult = WorksheetFunction.SUM(rCell) + vResult
        End If
       Next rCell
    Else
        For Each rCell In rRange
        If rCell.Interior.ColorIndex = lCol Then
                vResult = 1 + vResult
        End If
       Next rCell
End If
ColorFunction = vResult
End Function
1
  • Nice but less exhaustive. If I build a UDF that do something if I have some color in a cell, changing the format of the cell without editing it wont trigger the UDF. The solution with now()works everytime.
    – lalebarde
    Jan 24, 2020 at 18:17
4

I hit this while hoping to update all user defined functions whenever an ActiveX textbox was changed even if they didn't affect linked cells which were used as inputs to the UDF. By accident I discovered that including any indirect cell reference in the function call appears to make it run whenever (seemingly) any other cell is edited. Presumably this is because excel has fewer guarantees about what it can trust to be unmodified when an indirect modifier is used. This is similar to Twisty Impersonator's second answer but adds less cruft to your vba code and formulas (so long as as least one range is used in the function).

Example: This function should always return the value in 'A2'

Public Function test(some_arg)
test = Sheets("Sheet1").Range("A2").Value
End Function

If you call it like =test(B1) it will not updated whenever A2 changes If you call it like =test(INDIRECT(ADDRESS(ROW(B1),COLUMN(B1))) it is functionally equivalent, and will update whenever A2 changes.

=test(INDIRECT("B1")) will also work and may be cleaner to understand for this example, but because "B1" is a string you won't be able to copy the formula across new values.

1
  • If the INDIRECT workaround suggested here is useful to someone and they need it to be copyable around cells, you can "cheat" and use =INDIRECT(CELL("address",B1)). But what you are essentially doing is just using that INDIRECT is a volatile function, so Application.Volatile is probably a better solution.
    – Therkel
    Mar 10, 2021 at 8:15
1

Format changes (which includes Color Changes) are not considered events worthwhile a recalculation in Excel. That is not related to user defined functions. Therefore, any format change will not result in a recalculation.

That is not arbitrary, but makes some sense, as normally format changes do not influence anything else, so a recalculation - which is expensive - would be wasted. Since Excel now allows VB to use format information and react to it, this can become an issue, as it is for you.

You might be able to set OnChange functions for all cells, and trigger a recalculation there, but a format change might just not fire this function either (I never tried).

1
  • The OP's problem has everything to do with how Excel recalculates UDFs. While you're correct that a formatting change doesn't trigger a recalculation, it's not true that just any recalculation will update his UDF. Understanding this difference is crucial to solving this problem correctly. Oct 23, 2017 at 3:15
1

None of these worked for me. I found that the only way I could get my UDF to calculate was to add the code below to the worksheet.activate event and point the focus to another sheet on open. Then when I clicked on the tab to the main sheet it worked.

Application.CalculateFullRebuild 
1

You can transform any UDF formula in a volatile one by just doing this on the Excel Formula Bar:

=IF(NOW()>0, ColorFunction(A1, A2))

In this case, because there is the volatile function NOW() in the formula, the Excel will always recalculate ColorFunction!

1
  • That's what I call smart. Even, it is somehow a strange behavior of Excel that forces us to use these dirty tricks. Jan 3, 2023 at 11:58
0

I was having the same problem, and I thought the only solution was to recalculate the file. For files with thousands of formulae, it took too long. But the following solution worked. In VBA editor under Workbook object and SheetChange procedure, paste the following code. It calculates the cell that is changed, which solves the problem and is also quick.

Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Target.Calculate
End Sub

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .