【问题标题】:#REF! in formula after merging a workbook in Excel#参考!在 Excel 中合并工作簿后的公式中
【发布时间】:2015-08-06 19:52:02
【问题描述】:

我正在使用 VBA 宏将 Excel 工作簿合并为一个“summary.xls”。宏从另一个打开的工作簿执行。这个原始工作簿有一些公式包含“summary”(like ='C:\[Summary.xls]Cell'!E3).的链接,对于合并的过程,原始工作簿“summary.xls”被删除并重写。用原始链接重写所有公式后,摘要有#ref!写在里面,坏了,不能自动更新(='C:\[Summary.xls]#REF'!E4).下面这段是导致错误的一段:

        Workbooks(Filename).Close (False) 'add False to close without saving
 '       Kill srcFile                      'deletes the file
        Filename = Dir()

有人有解决问题的建议吗?

整个代码基于该建议:

Option Explicit

Function IsSheetEmpty(sht As Worksheet) As Boolean
    IsSheetEmpty = Application.WorksheetFunction.CountA(sht.Cells) = 0
End Function

Sub GetSheets()
    Dim Path, Filename As String
    Dim Sheet As Worksheet
    Dim newBook As Workbook
    Dim appSheets As Integer
    Dim srcFile As String
    Dim dstFile As String

    Application.ScreenUpdating = False  'go faster by not waiting for display

    '--- create a new workbook with only one worksheet
    dstFile = ActiveWorkbook.Path & "AllSheetsHere.xlsx"
    If Dir(dstFile) <> "" Then
        Kill dstFile     'delete the file if it already exists
    End If
    appSheets = Application.SheetsInNewWorkbook  'saves the default number of new sheets
    Application.SheetsInNewWorkbook = 1          'force only one new sheet
    Set newBook = Application.Workbooks.Add
    newBook.SaveAs dstFile
    Application.SheetsInNewWorkbook = appSheets  'restores the default number of new sheets

    Path = "C:\Temp\"
    Filename = Dir(Path & "*.xls?")  'add the ? to pick up *.xlsx and *.xlsm files
    Do While Filename <> ""
        srcFile = Path & Filename
        Workbooks.Open Filename:=srcFile, ReadOnly:=True
        For Each Sheet In ActiveWorkbook.Sheets
            '--- potentially check for blank sheets, or only sheets
            '    with specific data on them
            If Not IsSheetEmpty(Sheet) Then
                Sheet.Copy After:=newBook.Sheets(1)
            End If
        Next Sheet
        Workbooks(Filename).Close (False) 'add False to close without saving
        Kill srcFile                      'deletes the file
        Filename = Dir()
    Loop
    '--- delete the original empty worksheet and save the book
    newBook.Sheets(1).Delete
    newBook.Save
    newBook.Close

    Application.ScreenUpdating = True 're-enable screen updates
End Sub

【问题讨论】:

    标签: excel vba reference merge


    【解决方案1】:

    工作簿中的内部工作表到工作表引用 (Book1.xlsx) 通常如下所示:

    =ABC!B23

    但是,如果您将具有该引用的工作表复制到新工作簿,Excel 会将其更改为外部引用,返回到原始工作簿:

    ='[Book1.xlsx]ABC'!B23

    您必须对要复制到单个新工作簿中的工作表中的引用设置一些限制:

    1. 目标工作簿中的所有工作表名称必须是唯一的
      • Book1 中名为“ABC”的工作表和 Book2 中名为“ABC”的工作表会导致目标工作簿中的引用冲突
      • 必须将其中一张工作表重命名为唯一的字符串
    2. 完全在工作簿内部的工作表到工作表引用可以转换为目标中的类似引用。对外部工作表的引用(在不同的工作簿中)可能会出现问题,并且可能需要处理大量额外的逻辑。

    一个选项是在执行Sheet.Copy 之后在工作表上执行通配符搜索和替换。这里的要求是任何被引用的工作表必须已经在目标书中的新工作表的本地。 (否则,“fixed-up”引用仍然会给您一个 #REF 错误。)

    Sub test()
        Dim area As Range
        Dim farea As Range
        '--- determines the entire used area of the worksheet
        Set area = Range("A1").Resize(Cells.Find(What:="*", SearchOrder:=xlRows, _
                               SearchDirection:=xlPrevious, LookIn:=xlValues).Row, _
                               Cells.Find(What:="*", SearchOrder:=xlByColumns, _
                               SearchDirection:=xlPrevious, LookIn:=xlValues).Column)
        '--- replaces all external references to make them internal references
        area.Replace What:="[*]", Replacement:=""
    End Sub
    

    另一个选项更简洁,也更巧妙。当您将工作表复制到新工作簿中时,如果您在单个操作中复制所有工作表,则 Excel 会将工作表到工作表引用保留为内部引用(并且不会用文件名前缀替换每个引用),因为它知道工作表引用将在新工作簿中。这是您代码中的解决方案:

    Option Explicit
    
    Function IsSheetEmpty(sht As Worksheet) As Boolean
        IsSheetEmpty = Application.WorksheetFunction.CountA(sht.Cells) = 0
    End Function
    
    Sub GetSheets()
        Dim i As Integer
        Dim Path, Filename As String
        Dim sh As Worksheet
        Dim newBook As Workbook
        Dim appSheets As Integer
        Dim srcFile As String
        Dim dstFile As String
        Dim dstPath As String
        Dim wasntAlreadyOpen As Boolean
        Dim name As Variant
        Dim allSheetNames As Dictionary  'check VBA Editor->Tools->References->Microsoft Scripting Runtime
        Dim newSheetNames As Dictionary
        Dim newNames() As String
    
        Application.ScreenUpdating = False  'go faster by not waiting for display
    
        '--- create a new workbook with only one worksheet
        dstFile = "AllSheetsHere.xlsx"
        dstPath = ActiveWorkbook.Path & "\" & dstFile
        wasntAlreadyOpen = True
        If Dir(dstPath) = "" Then
            '--- the destination workbook does not (yet) exist, so create it
            appSheets = Application.SheetsInNewWorkbook  'saves the default number of new sheets
            Application.SheetsInNewWorkbook = 1          'force only one new sheet
            Set newBook = Application.Workbooks.Add
            newBook.SaveAs dstPath
            Application.SheetsInNewWorkbook = appSheets  'restores the default number of new sheets
        Else
            '--- the destination workbook exists, so ...
            On Error Resume Next
            wasntAlreadyOpen = False
            Set newBook = Workbooks(dstFile)             'connect if already open
            If newBook Is Nothing Then
                Set newBook = Workbooks.Open(dstPath)    'open if needed
                wasntAlreadyOpen = True
            End If
            On Error GoTo 0
            '--- make sure to delete any/all worksheets so we're only left
            '    with a single empty sheet named "Sheet1"
            Application.DisplayAlerts = False            'we dont need to see the warning message
            Do While newBook.Sheets.Count > 1
                newBook.Sheets(newBook.Sheets.Count).Delete
            Loop
            newBook.Sheets(1).name = "Sheet1"
            newBook.Sheets(1).Cells.ClearContents
            newBook.Sheets(1).Cells.ClearFormats
            Application.DisplayAlerts = True             'turn alerts back on
        End If
    
        '--- create the collections of sheet names...
        '    we need to make sure that all of the sheets added to the newBook have unique
        '    names so that any formula references between sheets will work properly
        '    LIMITATION: this assumes sheet-to-sheet references only exist internal to
        '                a single workbook. External references to sheets outside of the
        '                source workbook are unsupported in this fix-up
        Set allSheetNames = New Dictionary
        allSheetNames.Add "Sheet1", 1
    
        Path = "C:\Temp\"
        Filename = Dir(Path & "*.xls?")  'add the ? to pick up *.xlsx and *.xlsm files
        Do While Filename <> ""
            srcFile = Path & Filename
            Workbooks.Open Filename:=srcFile, ReadOnly:=True
            '--- first make sure all the sheet names are unique in the destination book
            Set newSheetNames = New Dictionary
            For Each sh In ActiveWorkbook.Sheets
                If Not IsSheetEmpty(sh) Then
                    '--- loop until we get a unique name
                    i = 0
                    Do While allSheetNames.Exists(sh.name)
                        sh.name = sh.name & "_" & i        'rename until unique
                        i = i + 1
                    Loop
                    allSheetNames.Add sh.name, i
                    newSheetNames.Add sh.name, i
                End If
            Next sh
            '--- we're going to copy ALL of the non-empty sheets to the new workbook with
            '    a single statement. the advantage of this method is that all sheet-to-sheet
            '    references are preserved between the sheets in the new workbook WITHOUT
            '    those references changed into external references
            ReDim newNames(0 To newSheetNames.Count - 1)
            i = 0
            For Each name In newSheetNames.Keys
                newNames(i) = name
                i = i + 1
            Next name
            ActiveWorkbook.Sheets(newNames).Copy After:=newBook.Sheets(1)
    
            Workbooks(Filename).Close (False) 'add False to close without saving
            Kill srcFile                      'deletes the file
            '--- get the next file that matches
            Filename = Dir()
        Loop
        '--- delete the original empty worksheet and save the book
        If newBook.Sheets.Count > 1 Then
            newBook.Sheets(1).Delete
        End If
        newBook.Save
        '--- leave it open if it was already open when we started
        If wasntAlreadyOpen Then
            newBook.Close
        End If
    
        Application.ScreenUpdating = True 're-enable screen updates
    End Sub
    

    【讨论】:

      【解决方案2】:

      如果您的工作簿中仍然引用了被引用的单元格(从您的示例中,您确实如此),并且如果您的所有 #REF!用于指向单个工作表的错误,有一个简单的修复方法。

      CTRL+H 调出替换功能。

      只需输入#REF!在“查找”框中,Sheet1 在“替换”框中,所有引用现在都将指向同一 summary.xls 工作簿中的 sheet1。

      【讨论】:

      • 另一本书中引用了 100 个工作表,或者 100 个工作表都引用了您的同一个 Summary.xls 工作簿?因为如果是后者,您可以一次在所有工作表上按 CTRL + H。否则,除了返回上一个文件并从头开始之外,没有简单的解决方案 - 确保不要删除对工作表的引用。
      • 那么对不起,没有简单的解决办法。您将需要返回到仍然具有完整引用的文件版本。定期检查工作簿中的#REF 错误是一种很好的做法,经常保存,以便在引用被切断时返回。
      • 谢谢....我有一个旧版本,这不是问题。我需要运行宏大约 100 次......每次引用被破坏时,,,,
      • @Nathalie 如果没有看到该代码,我们会情不自禁。什么代码会产生问题?如果你运行 100 倍宏,你应该能够以某种方式自动化它。
      • 如果我打开工作簿 summary.xls 并清除它而不是删除它会改变什么吗?试图实施但失败得很惨......
      【解决方案3】:

      我添加了一个包含参考公式的进一步工作簿。在删除和汇总工作表的整个过程中,这个是关闭的。此后新工作簿打开,因此避免了引用错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-16
        • 2016-07-13
        • 1970-01-01
        • 2016-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多