How can a VBA macro find which cells are being referenced in a formula

phg

Board Regular
Joined
Jul 4, 2007
Messages
81
I have a spreadsheet with a number of different formulae on it. I have a macro which I want to extract which cells are referenced in the formula in the active cell and then branch based on the which cell is referenced.

Is there a way to find the referenced cells without parsing the formula string?
 

Excel Facts

Shade all formula cells
To shade all formula cells: Home, Find & Select, Formulas to select all formulas. Then apply a light fill color.
Yes it works and it is enough for my purpose. Nevertheless, thanks for the link. Thanks also for the idea.
 
Upvote 0
Mike's original code met my needs since I was only looking for Precedents on the same worksheet. However I tested Jibse's code and modified it to deal with some limitations - i.e. it didn't return the Precedent/Dependent if it was on another sheet that was not open.

I had generated a long explanation of how it worked but MrExcel logged me out before I could paste it. Here is the short version - if anyone wants more detail I can provide it:

If you use
Code:
.NavigateArrow True, pN, qN
for a Precedent on a Worksheet that is not open it returns the cell you are starting from. I added a check to find if the cell returned and the one you are calling from are the same. If so I go through the list of all Workbooks linked with the current one, check if they are open and if not check if the filename is in the formula for the current cell. If the filename is there, open it and redo the .NavigateArrow command to find the Precedent.

The macro makes a few assumptions:

.DirectDependents.Count returns the number of Precedents on the current worksheet plus 1 if there are external Precedents.

qN goes from 1 to the number of external precedents but there is no way to get the number of external precedents from Excel so you have to increment qN until it fails.

I tested the macro under Excel 2002 and would be interested if others can get it to work for other versions of Excel and if they have any comments about my assumptions. I do not differentiate between external Precedents on a different Worksheet and ones on a different Workbook but that is a simple addition.

Note the macro doesn't work if the cell contains a circular reference to itself.

Code:
Sub doit()
'  Test routine
Dim rng As Range, strr As String
    Set rng = Selection
    If MsgBox("Check Current Cell for Precedents?", vbOK) = vbOK Then
        strr = oneCellsDependents(rng, True)
        MsgBox strr
    End If
    If MsgBox("Check Current Cell for Dependents?", vbOK) = vbOK Then
        strr = oneCellsDependents(rng, False)
        MsgBox strr
    End If
End Sub
 
Function oneCellsDependents(ByVal inRange As Range, Optional doPrecedents As Boolean) As String
Dim inAddress As String, returnSelection As Range, NavRng As Range, WSname As Variant, WSnameStr As String
Dim i As Long, pCount As Long, pTot As Long, qCount As Long, qCountTot As Long, AddrStr As String
Dim WB As Workbook, OpenFlg As Boolean
    Set returnSelection = inRange
    inAddress = fullAddress(inRange, True)
 
    Application.ScreenUpdating = False
    On Error Resume Next
    pTot = 0
' the number of precedents/dependents is the number on the current worksheet
                            ' plus one if there are any on another worksheet or workbook
    If (doPrecedents) Then
        pTot = inRange.DirectPrecedents.Count
    Else
        pTot = inRange.DirectDependents.Count
    End If
    If pTot = 0 Then
        oneCellsDependents = IIf(doPrecedents, "Cell has no Precedents", "Cell has no Dependents")
        Exit Function
    End If
    On Error GoTo 0
    With inRange
        .ShowPrecedents
        .ShowDependents
        pCount = 1
        qCount = 1
        qCountTot = 0 ' counts the number of dependents/precedents found
        Do
            On Error Resume Next
            Set NavRng = .NavigateArrow(doPrecedents, pCount, qCount)
            If (Err.Number <> 0) Then  ' you don't know how many external references there are
                                        ' so just keep trying until you ge an error
                pCount = pCount + 1     ' move on
                qCount = 1
                Err.Clear
                Set NavRng = .NavigateArrow(doPrecedents, pCount, qCount)
            End If
            On Error GoTo 0  ' go back to generating errors
 
            If (fullAddress(NavRng, True) = inAddress) Then  ' if the destination workbook is not open
                        ' there is no error but it returns the cell you are starting from
                        ' if you find one give the user the option of trying to open it
                        ' get a list of all the other workbooks and check each of them against the formula
                        ' if you find a match you open the workbook and go back again to check the precedent
                        ' if you choose not to open the workbook it is counted but not listed
                If (MsgBox("Formula contains reference to external Workbook that is not open" & vbCrLf _
                    & "Do you wish to attempt to open the Workbook", vbYesNo) = vbYes) Then
                    For Each WSname In ActiveWorkbook.LinkSources(xlExcelLinks)
 
                        WSnameStr = "[" & Mid(WSname, InStrRev(WSname, "\") + 1, 999) & "]"
                        If (InStr(1, returnSelection.Formula, WSnameStr, vbTextCompare) > 0) Then
                            OpenFlg = True
                            For Each WB In Workbooks
                                If "[" & WB.Name & "]" = WSnameStr Then
                                    OpenFlg = False
                                End If
                            Next
                            If (OpenFlg) Then
                                Workbooks.Open WSname
                                Exit For
                            End If
                        End If
                    Next
                    qCountTot = qCountTot - 1
                Else
                    qCount = qCount + 1
                End If
            ElseIf ((NavRng.Parent.Name <> inRange.Parent.Name) Or _
                (NavRng.Parent.Parent.Name <> inRange.Parent.Parent.Name)) Then
                    ' check if both the worksheet name and workbook name match
                    ' if not it is an external reference so include workbook name and worksheet name
                qCount = qCount + 1
                oneCellsDependents = oneCellsDependents & fullAddress(NavRng, True) & Chr(13)
            Else  ' reference is local so only report the address
                oneCellsDependents = oneCellsDependents & fullAddress(NavRng, False) & Chr(13)
                pCount = pCount + 1
                qCount = 1
            End If
            qCountTot = qCountTot + 1
        Loop While (pCount <= pTot)
        oneCellsDependents = fullAddress(inRange, False) & " on worksheet " & inRange.Parent.Name & "has " & qCountTot & _
            IIf(doPrecedents, " precedent " & IIf(qCountTot = 1, "range.", "range(s)."), " dependent " & _
            IIf(qCountTot = 1, "range.", "range(s).")) & vbCrLf & vbCrLf & oneCellsDependents
        .Parent.ClearArrows
    End With
 
    Rem return selection to where it was
    With returnSelection
        .Parent.Activate
        .Select
    End With
End Function
 
Function fullAddress(inRange As Range, ExtMode As Boolean) As String
    With inRange
        fullAddress = .Address(external:=ExtMode)
    End With
End Function
 
Upvote 0
Hello,

I have just tested it on Excel 2003 and 2007 (french versions). Here is the result :
1. It seems that it doesn't work if there is no precedent on the same sheet than the selection. It seems because if I haven't any precedent on the same sheet, I get the result 'Cell has no precedent' and if I add a reference in my formula, I get the following error :
2. I get the error 13 on this line
For Each WSname In ActiveWorkbook.LinkSources(xlExcelLinks)
 
Upvote 0
1) I missed the problem of only external links in the cell. Unfortunately I can't see any way to make that check without opening all links.

2) The error is a type mismatch. According to the documentation LinkSources with type xlExcelLinks should only return a workbook object so the types should match. However since the problem exists would suggest changing WSname to type object and then adding a check to see if it is a Workbook and if not skip the rest of the "open workbook " code.
 
Upvote 0
If you look at the code in the link in post11, there is code to parse a formula to extract references to closed workbooks.

While this is an interesting problem, I that if finding a cell's precedents in a closed workbook is important, then a better overall solution should be investigated. This would be a good point to wonder "is there a better overall approach".
 
Upvote 0
I agree with Mike. While parsing the string should work it is not very elegant. I'm sure Excel itself keeps track of all the precedents for a cell. The question is do they make that information available to the user and if so how?
 
Upvote 0
A couple of additional point about Precedents that I have noted.

.Precedents.Count gives the number of cells on the current sheet referenced in the formula not the number of different references. i.e. SUM(A1:A3) has a count of 3 and SUM(A1:A3)*A1*A2*A3 also has a count of 3 so it is not of much use in finding the Precedents.

The algorithm I used in Post13 will cause an error if a file name is also a string in the formula. The parsing routine used in Post11 also gives an error if there is a valid filename in a string.

The routine in Post13 can be used with the following modifications:

Check if .Precedent.Count is non zero (it never gives zero, the other option is an error). If it is non zero proceed as given except loop through .NavigateArrows, TRUE, pN increasing pN from pN = 2 until it returns the starting cell,

If .Precedent.Count gives an error it means there are either no Precedents or all Precedents are on a different sheet. Copy the formula to a blank cell MyRef and append a known reference to it. i.e.

MyRef.Formula=MyRef.Formula & "&$A$1"

Use the same routine to find all the Precedents in MyRef and discard Precedent $A$1

Finally blank MyRef again.
 
Upvote 0
I copied the code above and tried to 'insert' it into a test workbook and couldn't or don't know how to get it to 'run.' It appears that I don't know enough about VBA to create the macro to run the Function. So I"m not sure the code will do what I am trying to accomplish.

What I'm trying to accomplish is the following:
I would like a macro that I can add to and run on spreadsheets I receive which identifies by highlighting the cells which contain values which have no precedents (i.e. the raw data assumptions or drivers for the spreadsheet). I'm okay with a cell which may contain a formula as long as the formula doesn't reference another cell, worksheet, or workbook. I would then like another macro which that returns the spreadsheet back to its original state (removes highlighting of cells and potentially removes the two macros). Based on my online research so far, there maybe a limitation with the macro being able to work outside of an single workbook. I think that will be okay, as long as I'm able to have access to any external referenced worksheets-workbooks within which I can run the macros individually.

If it is not already apparent, I have very little knowledge or experience with VBA code. All help appreciated.
 
Upvote 0
I copied the code above and tried to 'insert' it into a test workbook and couldn't or don't know how to get it to 'run.' It appears that I don't know enough about VBA to create the macro to run the Function. So I"m not sure the code will do what I am trying to accomplish.

What I'm trying to accomplish is the following:
I would like a macro that I can add to and run on spreadsheets I receive which identifies by highlighting the cells which contain values which have no precedents (i.e. the raw data assumptions or drivers for the spreadsheet). I'm okay with a cell which may contain a formula as long as the formula doesn't reference another cell, worksheet, or workbook. I would then like another macro which that returns the spreadsheet back to its original state (removes highlighting of cells and potentially removes the two macros). Based on my online research so far, there maybe a limitation with the macro being able to work outside of an single workbook. I think that will be okay, as long as I'm able to have access to any external referenced worksheets-workbooks within which I can run the macros individually.

If it is not already apparent, I have very little knowledge or experience with VBA code. All help appreciated.

Indicators are I missed the window within which I could edit my message above. The code I tried to copy and use was the one with post#8. I missed the second page of the thread. Should this be a macro or an Add-In? Is it enough to say a numeric cell without a precedent? Or, do I need to also say numeric cells with a number, and any cell which contains a constant even if there is a referenced cell included in the formula?
 
Upvote 0

Forum statistics

Threads
1,215,330
Messages
6,124,307
Members
449,151
Latest member
JOOJ

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top