excelvbaexcel-charts

Trace Dependents for cells used in charts


How can we use VBA, to determine if a particular Excel cell is used in a chart elsewhere in the Workbook?

I regularly use the built-in Trace Dependents function to see how readily I can change/remove cells, but it only indicates which cell functions refer to the cell.

(There's a related old, unanswered question at Mr. Excel)


Solution

  • There is, as far as I know, nothing build-in, you will need to use some VBA code.
    You will need to understand the object model of a chart in Excel/VBA:

    A worksheet has a collection of DataSheetObjects.
    A DataSheetObject is the container for the chart itself, stored as propery Chart.
    The chart contains a collection of data series. The visible series can be accessed with SeriesCollection, all (even the hidden) series with FullSeriesCollection.

    Unfortunately, it is a little bit tricky to access the range that is used in a series. You have to use the property Formula, that looks something like this: =SERIES("MyName",Sheet1!$A$4:$A$7,Sheet1!$B$4:$B$7,1).

    Lets quickly look at the pieces: It contains basically 4 pieces, separated with a comma:
    (0) the series name: "MyName" (With the Prefix =SERIES(, but we don't care).
    (1) the range holding the x-axis values: Sheet1!$A$4:$A$7
    (2) the range holding the values: Sheet1!$B$4:$B$7,1
    (3) the index: 1 (plus the closing ) )

    We are interested in the two ranges. So what we have to do is to write a functiont that extract this ranges and compare it with the cell you are interested in. The following function checks this for one chart:

    Function isCellUsedInChart(cell As Range, ch As Chart) As Boolean
        Dim s As Series
        For Each s In ch.FullSeriesCollection
            ' Debug.Print s.Formula
            Dim pieces() As String
            pieces = Split(s.Formula, ",")
            
            Dim i As Long
            For i = 1 To 2
                Dim p As Long, sheetname As String, seriesAdr As String, seriesRange As Range
                p = InStr(pieces(i), "!")
                ' Extract sheetname
                sheetname = Left(pieces(i), p - 1)
                If sheetname <> cell.Parent.Name Then Exit Function     ' different sheet.
                
                ' Extract data range
                seriesAdr = Mid(pieces(i), p + 1)
                Dim ws As Worksheet
                Set ws = cell.Parent
                Set seriesRange = ws.Range(seriesAdr)
                
                If Not Intersect(seriesRange, cell) Is Nothing Then
                    isCellUsedInChart = True
                    Exit Function
                End If
            Next i
        Next s
    End Function
    

    Now all you have to do is to loop over all charts of all sheets, or with other words, of all ChartObjects of all Worksheets.

    A small addition: You might have charts as a sheet in a workbook. You can access them using the Charts-collection of the workbook. For this, the 2nd loop in the following function is used.

    Function isCellUsedInAnyChart(cell As Range) As Boolean
    
        Dim wb As Workbook, ws As Worksheet, co As ChartObject
        Set wb = cell.Parent.Parent
        ' Loop over all worksheets
        For Each ws In wb.Worksheets
            For Each co In ws.ChartObjects
                If isCellUsedInChart(cell, co.Chart) Then
                    isCellUsedInAnyChart = True
                    Exit Function
                End If
            Next
        Next
        Dim ch As Chart
        ' Loop over all chart sheets
        For Each ch In wb.Charts
             If isCellUsedInChart(cell, ch) Then
                isCellUsedInAnyChart = True
                Exit Function
            End If
        Next
    End Function