Как воссоздать лист и сохранить ссылки в силе? - PullRequest
1 голос
/ 10 июля 2019

У меня есть клиент, который держит в руках кучу рабочих листов, которые должны быть стандартизированы.Они созданы из импорта файлов CSV.По сути, мне нужно заменить текущие листы руководства, пока на них ссылаются из другой вкладки, не прерывая текущие ссылки.

Я свел проблему к одной книге с 2 листами.Ячейка A1 листа Sheet1 ссылается на ячейку A1 листа Sheet2, которая содержит строку «Sheet2A1CellData»

Все, что было закомментировано ниже, было опробовано, включая Application.Volatile и Application.Calculation.

Option Explicit
Sub TestSheet2Delete()
  Dim TmpSheet2 As Worksheet: Set TmpSheet2 = Sheets("Sheet2")

  'Application.Volatile

  If TmpSheet2 Is Nothing Then
    Exit Sub
  End If

  'Application.Calculation = False

  Application.DisplayAlerts = False
  TmpSheet2.Delete
  Application.DisplayAlerts = True

  Set TmpSheet2 = Worksheets.Add(After:=Sheets("Sheet1"))

  If TmpSheet2 Is Nothing Then
    Exit Sub
  End If

  TmpSheet2.Name = "Sheet2"
  TmpSheet2.Range("A1").Value = "Sheet2A1CellData"

  'Application.Calculation = True
End Sub

Sheet1 A1 первоначально был =Лист2! А1.Когда я запускаю указанную выше функцию из VBE, ячейка Листа A1 устанавливается на = # REF! A1.

Как сохранить действительную ссылку после замены листа?

Очевидно, что реальная проблема намного больше, и для повторного импорта данных CSV требуется обновить 132 000 ячеек.6000 строк х 22 колонны.

Спасибо за любую помощь.

Ответы [ 3 ]

0 голосов
/ 11 июля 2019

Спасибо, что задали действительно хороший вопрос.

Прежде всего, отказ от ответственности: это не прямое решение, а обходной путь, который мы должны были принять много лет назад.

Точно такая же проблема возникла на моем рабочем месте (буквально заставило нас вырвать свои волосы), и мы также попытались перейти на iNDIRECT.Но поскольку формулы в рабочих листах являются сложными, мы не смогли заменить их на INDIRECT.Таким образом, вместо длительной ручной замены сотен формул на рабочем листе, мы использовали для вставки временного листа и изменения ссылки на формулы на этот лист.После импорта нового листа и переименования его в название старого листа формулы были возвращены к исходному.Я попытался воспроизвести используемый код (поскольку у меня нет доступа к тем же файлам сейчас).Мы использовали только Sub ChangeFormulas, здесь я использовал то же самое в соответствии с вашим кодом.

Option Explicit
Sub TestSheet2Delete()
Dim Wb As Workbook
Dim Ws As Worksheet, Ws1 As Worksheet, Ws2 As Worksheet
Dim Xstr As String, Ystr As String
Set Wb = ThisWorkbook
Set Ws = Wb.Sheets("Sheet1")

Xstr = "Sheet2"
Ystr = "TempSheetX"
Set Ws1 = Wb.Sheets(Xstr)

Set Ws2 = Worksheets.Add(After:=Ws)
Ws2.Name = Ystr
DoEvents
ChangeFormulas Ws, Xstr, Ystr

Application.DisplayAlerts = False
Ws1.Delete

' Now again add another sheet with Old name and change formulas back to Original
Set Ws1 = Worksheets.Add(After:=Ws)
Ws1.Name = Xstr
DoEvents
ChangeFormulas Ws, Ystr, Xstr
Ws2.Delete

Application.DisplayAlerts = True

End Sub
Sub ChangeFormulas(Ws As Worksheet, Xstr As String, Ystr As String)
Dim Rng As Range, C As Range, FirstAddress As String
Set Rng = Ws.UsedRange
With Rng
    Set C = .Find(What:=Xstr, LookIn:=xlFormulas)
    If Not C Is Nothing Then
        FirstAddress = C.Address
        Do
            C.Formula = Replace(C.Formula, Xstr, Ystr)
            Set C = .FindNext(C)
            If C Is Nothing Then Exit Do
            If C.Address = FirstAddress Then Exit Do
        Loop
    End If
End With

End Sub

Еще один самый простой обходной путь - вообще не удалять лист, импортировать CSV и полностью копироватьлист на рассматриваемый лист. Однако это полностью зависит от фактических условий работы с CSV и всеми.

0 голосов
/ 17 июля 2019

Мой оригинальный ответ не работает для несмежных клеток.Тем не менее, мне очень нравится Range to Variants, а затем я возвращаюсь к паттерну Range.Очень быстро.Поэтому я переписал свой исходный ответ в более многократно используемый код, который тестирует с использованием несмежных ячеек.

Function PreserveFormulaeInVariantArr(ByVal aWorksheet As Worksheet, _
                                      ByVal aIsNoFormulaErr As Boolean, _
                                      ByRef aErrStr As String) As Variant
  Dim TmpRange As Range
  Dim TmpAreaCnt As Long
  Dim TmpVarArr As Variant
  Dim TmpAreaVarArr As Variant

  PreserveFormulaeInVariantArr = Empty

  If aWorksheet Is Nothing Then
    aErrStr = "PreserveFormulaeInVariantArr: Worksheet is Nothing."
    Exit Function
  End If

  Err.Clear
  On Error Resume Next
  Set TmpRange = aWorksheet.Cells.SpecialCells(xlCellTypeFormulas)
  If Err.Number <> 0 Then 'No Formulae.
    PreserveFormulaeInVariantArr = Empty
    If aIsNoFormulaErr Then
      aErrStr = "PreserveFormulaeInVariantArr: No cells with formulae."
    End If
    Exit Function
  End If

  TmpAreaVarArr = Empty
  On Error GoTo ErrLabel
  ReDim TmpVarArr(1 To TmpRange.Areas.Count, 1 To 2)

  For TmpAreaCnt = LBound(TmpVarArr) To UBound(TmpVarArr)
    TmpVarArr(TmpAreaCnt, 1) = TmpRange.Areas(TmpAreaCnt).Address 'Set 1st Element to Range
    TmpAreaVarArr = TmpRange.Areas(TmpAreaCnt).Formula 'Left TmpArrVarArr for Debugging
    TmpVarArr(TmpAreaCnt, 2) = TmpAreaVarArr 'Creates Jagged Array
  Next TmpAreaCnt

  PreserveFormulaeInVariantArr = TmpVarArr

  Exit Function
ErrLabel:
  aErrStr = "PreserveFormulaeInVariantArr - Error Number: " + CStr(Err.Number) + " Error Description: " + Err.Description
End Function

Function RestoreFormulaeFromVariantArr(ByVal aWorksheet As Worksheet, _
                                       ByVal aIsEmptyAreaVarArrError As Boolean, _
                                       ByVal aAreaVarArr As Variant, _
                                       ByRef aErrStr As String) As Boolean
  Dim TmpVarArrCnt As Long
  Dim TmpRange As Range
  Dim TmpDim1Var As Variant
  Dim TmpDim2Var As Variant
  Dim TmpDim2Cnt As Long
  Dim TmpDim2UBound As Long

  RestoreFormulaeFromVariantArr = False

  On Error GoTo ErrLabel

  If aWorksheet Is Nothing Then
    Exit Function
  End If

  If IsEmpty(aAreaVarArr) Then
    If aIsEmptyAreaVarArrError Then
      aErrStr = "RestoreFormulaeFromVariantArr: Empty array passed."
    Else
      RestoreFormulaeFromVariantArr = True
    End If
    Exit Function
  End If

  For TmpVarArrCnt = 1 To UBound(aAreaVarArr)
    TmpDim1Var = aAreaVarArr(TmpVarArrCnt, 1) 'This is always the range.
    TmpDim2Var = aAreaVarArr(TmpVarArrCnt, 2) 'This can be a Variant or Variant Array
    aWorksheet.Range(TmpDim1Var).Formula = TmpDim2Var
  Next TmpVarArrCnt

  RestoreFormulaeFromVariantArr = True

  Exit Function
ErrLabel:
  aErrStr = "PreserveFormulaeInVariantArr - Error Number: " + CStr(Err.Number) + " Error Description: " + Err.Description
End Function

Sub TestPreserveFormulaeInVariantArr()
  Dim TmpPreserveFormulaeArr As Variant
  Dim TmpErrStr As String
  Dim TmpIsNoFormulaErr As Boolean: TmpIsNoFormulaErr = True 'Change If Desired
  Dim TmpEmptySheet1 As Boolean: TmpEmptySheet1 = False 'Change If Desired
  Dim TmpSheet1 As Worksheet: Set TmpSheet1 = Sheets("Sheet1")
  Dim TmpSheet2 As Worksheet

  Err.Clear
  On Error Resume Next
  Set TmpSheet2 = Sheets("Sheet2")
  On Error GoTo 0

  'Always Delete Sheet2
  If (TmpSheet2 Is Nothing) = False Then
    Application.DisplayAlerts = False
    TmpSheet2.Delete
    Application.DisplayAlerts = True
    Set TmpSheet2 = Nothing
  End If

  If TmpSheet2 Is Nothing Then
    Set TmpSheet2 = Worksheets.Add(After:=Sheets("Sheet1"))
    TmpSheet2.Name = "Sheet2"
    TmpSheet2.Range("A1") = "Sheet2A1"
    TmpSheet2.Range("B1") = "Sheet2A1"
    TmpSheet2.Range("C4") = "Sheet2C4"

    If TmpEmptySheet1 Then
      TmpSheet1.Cells.ClearContents
    Else
      TmpSheet1.Range("A1").Formula = "=Sheet2!A1"
      TmpSheet1.Range("B1").Formula = "=Sheet2!B1"
      TmpSheet1.Range("C4").Formula = "=Sheet2!C4"
    End If
  End If

  TmpPreserveFormulaeArr = PreserveFormulaeInVariantArr(TmpSheet1, TmpIsNoFormulaErr, TmpErrStr)

  If TmpErrStr <> "" Then
    MsgBox TmpErrStr
    Exit Sub
  End If

  'Break Formulae and Cause #Ref Violation
  Application.DisplayAlerts = False
  TmpSheet2.Delete
  Application.DisplayAlerts = True

  'Add Sheet2 Back
  Set TmpSheet2 = Worksheets.Add(After:=Sheets("Sheet1"))
  TmpSheet2.Name = "Sheet2"
  TmpSheet2.Range("A1") = "Sheet2A1"
  TmpSheet2.Range("B1") = "Sheet2A1"
  TmpSheet2.Range("C4") = "Sheet2C4"

  'Restore Formulas Back to Sheet1
  If RestoreFormulaeFromVariantArr(TmpSheet1, TmpIsNoFormulaErr, TmpPreserveFormulaeArr, TmpErrStr) = False Then
    MsgBox TmpErrStr
    Exit Sub
  End If
End Sub

TestPreserveFormulaeInVariantArr может быть запущен в VBE с параметрами для установки пустых значений.Любые комментарии приветствуются.

0 голосов
/ 10 июля 2019

ПОСЛЕ того, как я разместил (конечно :-)), эта ссылка появилась справа: Сохранить ссылки , в которых рекомендуется использовать INDIRECT. Теперь я изменил Sheet1 A1 на =INDIRECT("Sheet2!"&"A1").

Я не уверен, зачем нужны именованные диапазоны, предложенные в ссылке. Вышеуказанный косвенный вызов работает без именованного диапазона.

Если это работает в более крупном проекте, я отмечу это как завершенное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...