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

现在已经确定解决scheme了

我是一个build立UDF的VBA新手。 这个UDF包含许多在工作簿中的其他工作表上引用Excel表格的查找函数,比如:

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

麻烦的是,如果另一个工作簿处于活动状态,那么当Excel重新计算公式时,将返回一个#VALUE错误。

我看到很多关于如何引用VBA和UDF中的其他工作簿和工作表的解决scheme,但是我不知道如何适当地限制这些表对象,以便他们将注意力集中在UDF所在的工作簿上。 请注意,我正在寻找一个依赖于工作表名称或工作簿文件名或path的解决scheme,因为所有这些都可能随时间而改变。

这是我的名字经理这个工作簿: 名称经理

这是整个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 

解决scheme(谢谢@DavidZemens)

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

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

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

但是,因为表的行为与命名范围不同(尽pipe在名称pipe理器中类似地出现),我需要像这样调用范围:

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

但是,我的理想解决scheme完全避免了命名表单,以防将来表单名称发生变化,所以certificate我可以使用表单CodeName (在我的例子sheet1 ):

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

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

我希望有一个优雅的解决scheme,如…

 thisworkbook.range("iterationtable") 

虽然这不起作用。

Range不是Workbook类的属性,它是Worksheet类的属性。

但是,命名范围可以通过Names集合来访问。 如果Name的范围是Workbook(我认为是默认的),那么你应该可以通过ThisWorkbook.Names("iterationtable").RefersToRange来访问它ThisWorkbook.Names("iterationtable").RefersToRange

如果Name的范围是一个特定的工作表,那么您将需要执行ThisWorkbook.__WORKSHEET__.Names("iterationtable")...而是,其中__WORKSHEET__是包含工作表。


上面这个是假设这是一个Name对象,但是在审查了Matt的截图之后,很明显这不是一个Name ,而是一个ListObject (table):

在这里输入图像说明

虽然这些出现在名称pipe理器中,并且可以类似地访问命名的范围,即:

 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时候,它有很好的文档logging和一个不正确的范围标识符的预期function: Range指的是任何表单在运行时是活动的,除非另有明确的范围。

这是如何避免臭名昭着的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 

最后:

包括避免引用指定范围所在的工作表。

好吧,如果用户将工作表名称从“背景表格”更改为其他任何内容,上面的代码仍然会失败。 有一些方法可以防止这种情况发生,例如locking工作表进行编辑和/或隐藏工作表(假设用户不需要向工作表input数据等),或者通过其CodeName引用工作表而不是它的Name 。 这利用了CodeName引用的事实, 工作表总是隐式地成为ThisWorkbook的一部分 。 要find工作表的CodeName ,在工程窗格的圆括号中:

在这里输入图像说明

在上面的解决scheme中,你可以改变这一行:

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

至:

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

虽然工作表的CodeName是读/写的(意思是一个精明的用户可以改变它,他们需要通过VBA或通过VBE手动执行,所以这在大多数情况下似乎不太可能)。

您是否尝试使用工作簿名称和工作表进行所有对工作簿的调用? 只使用“范围(”任何“)”将导致程序查看任何工作簿当前活动的活动工作表。

尝试将呼叫更改为这种格式:

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

将“CorrectWorkbookName”更改为具有指定范围的书籍名称的位置,并将“CorrectWorksheetName”更改为包含指定范围的工作表的名称。

或者也许或许激活正确的工作簿

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