当另一个工作簿处于活动状态时,UDF 引用命名 table 错误

UDF referring to named table errors when another workbook is active

既然已经确定了解决方案,为清楚起见进行了编辑

我是一个 VBA 构建 UDF 的新手。这个UDF涉及到一些vlookup函数,引用工作簿中其他工作sheet的excel table,例如:

Twirecol = Application.WorksheetFunction.VLookup(i, Range("iterationtable"), 2, False)

问题是,如果另一个工作簿处于活动状态,当 excel 重新计算公式 returns 时出现 #VALUE 错误。

我看到很多关于如何在 VBA 和 UDF 中引用其他工作簿和工作 sheet 的解决方案,但我不知道如何适当地限定这些 table 对象的范围,所以他们始终专注于 UDF 所在的工作簿。请注意,我正在寻找 取决于工作 sheet 名称或工作簿文件名或路径的解决方案,因为所有这些都可能随时间而改变。

这是我的工作簿管理员姓名:Names Manager

这是完整的 UDF 代码:

Public Function voltagedrop(trenchlength As Integer, intlength As Integer) As String
Application.Volatile

Dim TLX As Integer
Dim ILX As Integer
Dim TVD As Single
Dim IVD As Single
Dim VD As Single
Dim Twirecol As Integer
Dim Iwirecol As Integer
Dim i As Integer


' Extended length variables account for extra length at end of strings
TLX = trenchlength + 10
ILX = intlength + 10

i = 0

Do
i = i + 1
    Twirecol = Application.WorksheetFunction.VLookup(i, Range("iterationtable"), 2, False)
    Iwirecol = Application.WorksheetFunction.VLookup(i, Range("iterationtable"), 3, False)

    ' Calculate voltage drops
    TVD = Application.WorksheetFunction.VLookup(TLX, Range("trenchtable"), Twirecol, False)
    IVD = Application.WorksheetFunction.VLookup(ILX, Range("inttable"), Iwirecol, False)
    VD = TVD + IVD

Loop Until VD < 0.025

VD = 100 * Round(VD, 4)
voltagedrop = Application.WorksheetFunction.VLookup(i, Range("iterationtable"), 4, False) & ": " & VD & "%"    

End Function

解决方案(感谢@DavidZemens)

(*大卫的完整答案在下面,这是我的总结)

如果这是一个传统的命名范围,而不是 table,我可以这样命名范围:

Twirecol = Application.WorksheetFunction.VLookup(i, ThisWorkbook.Names("iterationtable").RefersToRange, 2, False)

但是,因为 tables 的行为与命名范围不同(尽管在名称管理器中显示类似),我需要这样调用范围:

Twirecol = Application.WorksheetFunction.VLookup(i, ThisWorkbook.Worksheets("Background Tables").ListObjects("iterationtable").Range, 2, False)

但是,我的理想解决方案完全避免命名 sheet,以防将来 sheet 名称发生变化,因此证明我可以使用 sheet CodeName 而不是(在我的例子中 sheet1):

Twirecol = Application.WorksheetFunction.VLookup(i, Sheet1.ListObjects("iterationtable").Range, 2, False)

为了简单起见,我直接在此示例代码中确定了范围,但根据 David 的建议,我的最终代码确实使用了一个范围变量集。

您是否尝试使用工作簿名称和 sheet 对工作簿进行所有调用?仅使用 "Range("whatever")" 将导致程序查看当前活动的任何工作簿的活动工作sheet。

尝试将调用更改为这种格式:

Twirecol = Application.WorksheetFunction.VLookup(i, CorrectWorkbookName.CorrectWorksheetName.Range("iterationtable"), 2, False)

将 "CorrectWorkbookName" 更改为具有指定范围的书名,并将 "CorrectWorksheetName" 更改为包含指定范围的 sheet 的名称。

I am hoping there is an elegant solution such as...

thisworkbook.range("iterationtable")

Although this does not work.

Range 不是 Workbook class 的 属性,它是 Worksheet class 的 属性 ].

但是,可以通过 Names 集合访问命名范围。如果 Name 的范围限于工作簿(我认为这是默认设置),您应该能够通过 ThisWorkbook.Names("iterationtable").RefersToRange.

访问它

如果 Name 的范围限定为特定作品sheet,那么您将需要执行 ThisWorkbook.__WORKSHEET__.Names("iterationtable")...,其中 __WORKSHEET__ 包含 [=98] =].


以上内容假设这是一个 Name 对象,但在查看 Matt 的屏幕截图后,很明显这实际上不是 Name,而是一个 ListObject (table).:

虽然这些出现在名称管理器中,并且同样可以访问命名范围,即:

MsgBox ActiveSheet.Range("table1").Address ' etc...

它们不是 Names 集合的成员(它们实际上是 ListObjects 集合的一部分),因此尝试像下面那样调用它们将引发 1004 错误:

MsgBox ActiveSheet.Names("table1").Address

要解决此问题,您需要完全限定 Range 对象,即:

ThisWorkbook.Worksheets("Background Tables").Range("iterationtable")

或:

ThisWorkbook.Worksheets("Background Tables").ListObjects("iterationtable").Range

Range("iterationtable") 有时起作用的原因(即,当它是 ActiveSheet 时,有据可查且正常,范围不当的标识符的预期功能:Range 指的是 always 到任何 sheet 在运行时 Active,除非另有明确的范围。

This 是一本关于如何避免臭名昭著的 1004 错误的入门读物,但归结为:适当地确定对象的范围,最好使用 variables代表他们。

Dim wsTables as Worksheet
Dim iterTable As Range
Dim trenchTable as Range
Dim intTable as Range

Set wsTables = ThisWorkbook.Worksheets("Background Tables") 'Declare the worksheet
With wsTables
    'Assigns each table's Range to a Range object variable:
    Set iterTable = .ListObjects("iterationtable").Range
    Set trenchTable = .ListObjects("Trench Table").Range
    Set intTable = .ListObjects("Int Table").Range
End With

With Application.WorksheetFunction
    Do
        i = i + 1
        Twirecol = .VLookup(i, iterTable, 2, False)
        Iwirecol = .VLookup(i, iterTable, 3, False)
        ' Calculate voltage drops
        TVD = .VLookup(TLX, trenchTable, Twirecol, False)
        IVD = .VLookup(ILX, iterTable, Iwirecol, False)
        VD = TVD + IVD
    Loop Until VD < 0.025

    VD = 100 * Round(VD, 4)
    voltagedrop = .VLookup(i, iterTable, 4, False) & ": " & VD & "%"
End With

最后:

including avoiding references to the sheet on which the named ranges are located.

OK,所以如果用户将 worksheet 名称从 "Background Tables" 更改为其他名称,上面的代码仍然会失败。有一些方法可以防止这种情况发生,例如锁定 sheet 进行编辑,and/or 隐藏作品 sheet(假设用户不需要 输入 数据到 sheet,等等),或通过 CodeName 而不是 Name 引用作品 sheet。这利用了 when referred to by CodeName, the Worksheet is always implicitly a part of ThisWorkbook 这一事实。要查找 Sheet 的 CodeName,它位于项目窗格中的括号中:

在上述解决方案中,您需要更改此行:

Set wsTables = ThisWorkbook.Worksheets("Background Tables") 'Declare the worksheet

收件人:

Set wsTables = Sheet2 '<~~ The CodeName goes here, modify as needed!

虽然 sheet 的 CodeName 是 read/write(这意味着精明的用户 可以 更改它,但他们需要做通过 VBA 或手动通过 VBE,因此在大多数情况下这似乎不太可能)。

或者事先激活正确的工作簿

Set wb = ThisWorkbook
wb.Activate
TVD = Application.WorksheetFunction.VLookup(TLX, Range("trenchtable"), Twirecol, False)