Programmatically select other sheet precedents or dependents in Excel

前端 未结 3 1552
我在风中等你
我在风中等你 2021-02-09 18:01

In Excel Ctrl+[ or ] will sometimes directly switch to another sheet to show the precedents or dependents in that sheet.

I want that pro

相关标签:
3条回答
  • 2021-02-09 18:57

    Mark did some good job, but this macro altogether did not go to 'dents in the same sheet and failed, when there were 'dents from multiple sheets, since the selection cannot be created from multiple sheet cells.

    I personally needed all this functionality to replace the "Ctrl + [" and "Ctrl + ]" quick shortcut functionality for jumping to precedents and dependents. Unfortunately, these shortcuts are completely unusable on international keyboard, where these square brackets are buried under AltGr (right Alt) combination and Excel does not allow either Ctrl+AltGr+8 and Ctrl+AltGr+8 to give the same result and also there is no way to remap the default shortcuts.

    So I improved the code of Mark slightly to fix these issues and removed the pop-up message from code, since I should know myself if I cannot select all 'dents, but I want the function to work smoothly without me having to click OK all the time. So the function just jumps to the sheet, which is linked first in the formula.

    I hope this is useful for others as well.

    The only thing what still bothers me is that while Application.ScreenUpdating = False Avoids jumping around the sheet and workbook, the arrows still keep blinking. Any way to avoid this?

    Option Explicit
    
    Private Sub GetOffSheetDents(ByVal doPrecedents As Boolean)
    'Main function, calling for separate function to find links to all cells to one of the input cells. Works for finding precedents for a whole selection (group of cells)
    'doPrecedents is TRUE, if we are searching for precedents and FALSE, if looking for dependents
    Dim InputCell As Range
    Dim results As Range
    Dim r As Range
    Dim sheet As Worksheet
    
    Application.ScreenUpdating = False
    
    For Each InputCell In Application.Intersect(ActiveSheet.UsedRange, Selection)
    'Cycle to go over all initially selected cells. If only one cell selected, then happens only once.
        Set r = oneCellDependents(InputCell, doPrecedents)
        ' r is resulting cells from each iteration of input cell to the function.
        If Not r Is Nothing Then      'if there were precedents/dependents
            If sheet Is Nothing Then  'if this is the first time.
                Set sheet = r.Worksheet
                Include results, r
            ElseIf Not sheet Is r.Worksheet Then 'if new precedent/dependent is on another worksheet, don't add to selection (gets lost)
            Else
                Include results, r
            End If
        End If
    Next
    Application.ScreenUpdating = True
    
    If results Is Nothing Then
        Beep
    Else
        results.Worksheet.Activate
        results.Select
    End If
    End Sub
    
    Sub GetOffSheetDependents()
    'Function defines, if we are looking for Dependents (False) or Precedents (True)
    GetOffSheetDents False
    
    End Sub
    
    Sub GetOffSheetPrecedents()
    'Function defines, if we are looking for Dependents (False) or Precedents (True)
    GetOffSheetDents True
    
    End Sub
    
    Private Function Include(ByRef ToUnion As Range, ByVal Value As Range) As Range
    If ToUnion Is Nothing Then
        Set ToUnion = Value
        ElseIf Value.Worksheet Is ToUnion.Worksheet Then 'if new precedent/dependent is on the same worksheet, then add to selection
                'if new precedent/dependent is on another worksheet, don't add to selection (gets lost)
            Set ToUnion = Application.Union(ToUnion, Value)
    End If
    Set Include = ToUnion
    End Function
    
    Private Function oneCellDependents(ByVal inRange As Range, Optional doPrecedents As Boolean) As Range
    'Function finds dependents for one of the selected cells. Happens only once, if initially only one cell selected.
    Dim inAddress As String, returnSelection As Range
    Dim i As Long, pCount As Long, qCount As Long
    Application.ScreenUpdating = False
    If inRange.Cells.Count <> 1 Then Error.Raise 13 'seems to check, that only one cell is handled, but does not seem to be necessary step.
    
    'remember selection
    Set returnSelection = Selection ' to keep initial selection for GetOffSheetDents function.
    inAddress = fullAddress(inRange) ' takes address of starting cell what is analyzed.
    pCount = 1
    
    With inRange   'all functions apply to this initial cell.
        .ShowPrecedents
        .ShowDependents
        .NavigateArrow doPrecedents, 1 ' go to first precedent (if first argument is true)/dependent. But why required?
        Do Until fullAddress(ActiveCell) = inAddress
            .NavigateArrow doPrecedents, pCount 'go to first precedent, then second etc.
            If ActiveSheet.Name <> returnSelection.Parent.Name Then ' checks, if the precedent is NOT on the same sheet
    
                Do
                    qCount = qCount + 1   'qCount follows external references, if arrow is external reference arrow.
                    .NavigateArrow doPrecedents, pCount, qCount 'go to first exteranl precedent, then second etc.
                    Include oneCellDependents, Selection
                    On Error Resume Next
                    .NavigateArrow doPrecedents, pCount, qCount + 1 'could remove this step and check for error before Include?
                    If Err.Number <> 0 Then Exit Do
                    On Error GoTo 0  ' not sure if this is used, since if there is error, then already Exit Do in previous step.
                Loop
                On Error GoTo 0 'not sure, if necessary, since just asked in loop.
            Else  ' if precedent IS ON the same sheet.
                Include oneCellDependents, Selection
            End If
            pCount = pCount + 1
            .NavigateArrow doPrecedents, pCount
        Loop
        .Parent.ClearArrows
    End With
    
    'return selection to where it was
    With returnSelection
        .Parent.Activate
        .Select
    End With
    
    End Function
    
    Private Function fullAddress(inRange As Range) As String
    'Function takes a full address with sheet name
    
    With inRange
        fullAddress = .Parent.Name & "!" & .Address
    End With
    End Function
    
    0 讨论(0)
  • 2021-02-09 18:58

    I found kaidobor's version of Mark Hurd's code exactly what I needed. I wrote a wrapper to document all the dependencies in the selected cells and insert them in a new sheet. My code just calls kaidobor's code and records the results.

    My use case: I have a complex spreadsheet (written by someone else) that I need to clean up. I want to delete some sheets that appear unnecessary but want to know where I'll be breaking formulas before deleting the sheets. This will create an index showing all the cells that are referenced in other sheets.

    Sub FindDependentsForThisSheet()
    ' Find all cells in the selection that have dependents on some other sheet
    ' Calls code by kaidobor
    ' January 9, 2017
    Dim rCurrent As String, strNoDependents As String, strDependents As String, strCurrrentParent As String
    Dim aDependents(1000, 4) As String ' Starting sheet, starting cell, referenced sheet, referenced cell
    Dim intArrayRows As Long
    strNoDependents = "No Dependents" & vbCrLf
    strDependents = "Dependents" & vbCrLf
    intArrayRows = 0
    Application.ScreenUpdating = False
    
    'Step through each cell in the current sheet (for each…)
    For Each cell In Selection.Cells
        ' improvement: step through just the cells that are selected in case I know some are not worth bothering with
        Range(cell.Address).Select
        rCurrent = ActiveCell.Address
        strCurrrentParent = ActiveCell.Parent.Name
        'Run GetOffSheetDependents() for each cell
        GetOffSheetDependents
        'GetOffSheetPrecedents
        'When GetOffSheetDependents() is done, if the ActiveCell.Address is not changed,
        'If (rCurrent = ActiveCell.Address And strCurrrentParent = ActiveCell.Parent.Name) Then ' We do care about links on the current sheet
        If (strCurrrentParent = ActiveCell.Parent.Name) Then ' Do not care about links on the current sheet
            'then nothing
            strNoDependents = strNoDependents & ActiveCell.Parent.Name + " - " + ActiveCell.Address & vbCrLf
        Else
            ' Stuff the array
            aDependents(intArrayRows, 0) = strCurrrentParent
            aDependents(intArrayRows, 1) = rCurrent
            aDependents(intArrayRows, 2) = ActiveCell.Parent.Name
            aDependents(intArrayRows, 3) = ActiveCell.Address
            intArrayRows = intArrayRows + 1
            strDependents = strDependents + strCurrrentParent + "!" + rCurrent + " referenced in " + ActiveCell.Parent.Name + "!" + ActiveCell.Address & vbCrLf
            '1 record ActiveCell.Address + parent.
            '2 return to home sheet and
            Sheets(strCurrrentParent).Select
            '3 record the address of the active cell
        End If
        If intArrayRows > 999 Then
            MsgBox "Too many cells, aborting"
            Exit Sub
        End If
    Next
    'Debug.Print strDependents
    'Debug.Print strNoDependents
    
    ' Store results in a new sheet
    If intArrayRows > 0 Then
        varReturn = NewSheetandPaste(aDependents)
        MsgBox ("Finished looking for dependencies. Created sheet with results. Found this many: " & intArrayRows)
    Else
        MsgBox ("Finished looking for dependencies, found none.")
    End If
    Application.ScreenUpdating = True
    End Sub
    ' ************************************************************************************************
    
    Function NewSheetandPaste(aPasteThis As Variant) '(strSheetName As String)
    ' Create new sheet and past strDependents
    Dim strName As String, strStartSheetName As String, n As Long
    'strName = strSheetName + "Dependents"
    strStartSheetName = ActiveSheet.Name
    strName = strStartSheetName + "Dependents"
    Sheets.Add After:=ActiveSheet
    ActiveSheet.Name = strName
    'Sheets("Sheet4").Name = "Sheet1Dependents"
    Range("A1").Value = "Dependents from " + strStartSheetName
    'ActiveCell.FormulaR1C1 = "Dependents from Sheet1"
    'Range("A2").Value = strPasteThis
    Range("A2").Value = "Starting Sheet"
    Range("B2").Value = "Starting Sheet Cell"
    Range("C2").Value = "Dependent Sheet"
    Range("D2").Value = "Dependent Sheet Cell"
    
    Range("A3").Select
    intLengthArray = UBound(aPasteThis) - LBound(aPasteThis) + 1
    n = 0
    'For n = 0 To intLengthArray
    While aPasteThis(n, 0) <> ""
        ActiveCell.Value = aPasteThis(n, 0)
        ActiveCell.Offset(0, 1).Select
        ActiveCell.Value = aPasteThis(n, 1)
        ActiveCell.Offset(0, 1).Select
        ActiveCell.Value = aPasteThis(n, 2)
        ActiveCell.Offset(0, 1).Select
        ActiveCell.Value = aPasteThis(n, 3)
        ActiveCell.Offset(1, -3).Select
        n = n + 1
    Wend
    
    NewSheetandPaste = True
    End Function
    
    0 讨论(0)
  • 2021-02-09 19:01

    After a fair bit of Googling I found it was solved in 2003.

    But I used the code from here.

    The problem is that Dependents and Precedents are Range properties, which can't refer to multiple worksheets.

    The solution uses NavigateArrow to locate the cross-sheet 'dents.

    Here's my code:

    Option Explicit
    
    Private Sub GetOffSheetDents(ByVal doPrecedents As Boolean)
    
    Dim c As Range
    Dim results As Range
    Dim r As Range
    Dim sheet As Worksheet
    Dim extra As Boolean
    
    For Each c In Application.Intersect(ActiveSheet.UsedRange, Selection)
        Set r = oneCellDependents(c, doPrecedents)
        If Not r Is Nothing Then
            If r.Worksheet Is ActiveSheet Then
                ' skip it
            ElseIf sheet Is Nothing Then
                Set sheet = r.Worksheet
                Include results, r
            ElseIf Not sheet Is r.Worksheet Then
                If Not extra Then
                    extra = True
                    MsgBox "More than one external sheet in " & IIf(doPrecedents, "Precedents", "Dependents") & ". Only displaying first sheet."
                End If
            Else
                Include results, r
            End If
        End If
    Next
    
    If results Is Nothing Then
        Beep
    Else
        results.Worksheet.Activate
        results.Select
    End If
    End Sub
    
    Sub GetOffSheetDependents()
    
    GetOffSheetDents False
    
    End Sub
    
    Sub GetOffSheetPrecedents()
    
    GetOffSheetDents True
    
    End Sub
    
    Private Function Include(ByRef ToUnion As Range, ByVal Value As Range) As Range
    If ToUnion Is Nothing Then
        Set ToUnion = Value
    Else
        Set ToUnion = Application.Union(ToUnion, Value)
    End If
    Set Include = ToUnion
    End Function
    
    Private Function oneCellDependents(ByVal inRange As Range, Optional doPrecedents As Boolean) As Range
    
    Dim inAddress As String, returnSelection As Range
    Dim i As Long, pCount As Long, qCount As Long
    
    If inRange.Cells.Count <> 1 Then Error.Raise 13
    
    Rem remember selection
    Set returnSelection = Selection
    inAddress = fullAddress(inRange)
    
    Application.ScreenUpdating = False
    With inRange
        .ShowPrecedents
        .ShowDependents
        .NavigateArrow doPrecedents, 1
        Do Until fullAddress(ActiveCell) = inAddress
            pCount = pCount + 1
            .NavigateArrow doPrecedents, pCount
            If ActiveSheet.Name <> returnSelection.Parent.Name Then
    
                Do
                    qCount = qCount + 1
                    .NavigateArrow doPrecedents, pCount, qCount
                    Include oneCellDependents, Selection
                    On Error Resume Next
                    .NavigateArrow doPrecedents, pCount, qCount + 1
                    If Err.Number <> 0 Then _
                        Exit Do
                    On Error GoTo 0
                Loop
                On Error GoTo 0
                .NavigateArrow doPrecedents, pCount + 1
            Else
                Include oneCellDependents, Selection
                .NavigateArrow doPrecedents, pCount + 1
            End If
        Loop
        .Parent.ClearArrows
    End With
    
    Rem return selection to where it was
    With returnSelection
        .Parent.Activate
        .Select
    End With
    Application.ScreenUpdating = True
    
    End Function
    
    Private Function fullAddress(inRange As Range) As String
    With inRange
        fullAddress = .Parent.Name & "!" & .Address
    End With
    End Function
    
    0 讨论(0)
提交回复
热议问题