Вопрос по excel, vba – Программно выбрать другие листовые прецеденты или иждивенцы в Excel
В ExcelCtrl+[ или же] иногда будет напрямую переключаться на другой лист, чтобы показать прецеденты или иждивенцы на этом листе.
Я хочу это программно, потому что я хочу получить прецеденты (или иждивенцы) выбора ячеек.
Range.Dependents
а такжеRange.Precedents
иметьдругие вопросы, но решение там не решает проблему дополнительного листа.
Марк проделал хорошую работу, но этот макрос вообще не пошел в «вмятины» на одном и том же листе и потерпел неудачу, когда были «вмятины» на нескольких листах, поскольку выборка не может быть создана из нескольких ячеек листа.
Мне лично понадобился весь этот функционал, чтобы заменить & quot; Ctrl + [& quot; и & quot; Ctrl +] & quot; функциональность быстрого ярлыка для перехода к прецедентам и иждивенцам. К сожалению, эти сочетания клавиш полностью недоступны на международной клавиатуре, где эти квадратные скобки скрыты под комбинацией AltGr (правый Alt), а Excel не позволяет Ctrl + AltGr + 8 и Ctrl + AltGr + 8, чтобы дать тот же результат, а также есть нет способа переназначить ярлыки по умолчанию.
Поэтому я немного улучшил код Марка, чтобы исправить эти проблемы, и удалил всплывающее сообщение из кода, так как я должен сам знать, не могу ли я выбрать все вмятины, но я хочу, чтобы функция работала плавно, не нажимая кнопку ОК. все время. Таким образом, функция просто переходит на лист, который связан первым в формуле.
Я надеюсь, что это полезно и для других.
Единственное, что меня до сих пор беспокоит, так это то, что хотя Application.ScreenUpdating = False позволяет избежать скачков по листу и рабочей книге, стрелки все еще продолжают мигать. Есть ли способ избежать этого?
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
inRange.Cells.Count <> 1
проверка - это просто оборонительное программирование.
Mark Hurd
On Error GoTo 0
, Это выключаетOn Error Resume Next
и вам нужно сделать это независимо от того, произошла ошибка или нет, таким образом, в двух местах.
Mark Hurd
Я нашел каидоборскую версию кода Марка Херда именно то, что мне было нужно. Я написал оболочку для документирования всех зависимостей в выбранных ячейках и вставил их в новый лист. Мой код просто вызывает код kaidobor и записывает результаты.
Мой вариант использования: у меня есть сложная электронная таблица (написанная кем-то другим), которую мне нужно очистить. Я хочу удалить некоторые листы, которые кажутся ненужными, но хочу знать, где я буду нарушать формулы, прежде чем удалять листы. Это создаст индекс, показывающий все ячейки, на которые есть ссылки на других листах.
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
После некоторого поиска в Google я обнаружил, что это было решено в2003.
Но я использовал код изВот.
Проблема в том, чтоDependents
а такжеPrecedents
являютсяRange
свойства, которые не могут относиться к нескольким рабочим листам.
Решение используетNavigateArrow
чтобы найти вмятины на поперечном листе.
Вот мой код:
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