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

时间:2017-05-15 15:52:28

标签: excel excel-vba user-defined-functions udf named-ranges vba

为了清晰起见而编辑,现已找到解决方案

我是构建UDF的VBA新手。此UDF涉及许多vlookup函数,这些函数引用工作簿中其他工作表上的excel表,例如:

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

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

我看到了很多关于如何在VBA和UDF中引用OTHER工作簿和工作表的解决方案,但我不知道如何适当地调整这些表对象的范围,以便它们专注于UDF所在的工作簿。请注意,我正在寻找依赖于工作表名称或工作簿文件名或路径的解决方案,因为所有这些都可能会随着时间的推移而发生变化。

以下是此工作簿的名称管理员: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)

(* David的完整答案如下,这是我的总结)

如果这是一个传统的命名范围,而不是一个表格,我可以像这样调用范围:

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

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

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

但是,我的理想解决方案完全避免命名工作表,以防工作表名称在将来发生变化,因此证明我可以使用工作表CodeName代替sheet1 ):

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

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

3 个答案:

答案 0 :(得分:1)

  

我希望有一个优雅的解决方案,比如......

thisworkbook.range("iterationtable")
     

虽然这不起作用。

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

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

如果Name的范围限定为特定工作表,那么您需要改为ThisWorkbook.__WORKSHEET__.Names("iterationtable")...,其中__WORKSHEET__是包含的工作表。

以上是假设这是一个Name对象而写的,但在审查了Matt的屏幕截图后,很明显这实际上并不是Name,而是ListObject(表)。:

enter image description here

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

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

他们不是Names集合的成员(他们实际上是ListObjects集合的一部分),因此尝试如下调用它们会引发1004错误:< / p>

MsgBox ActiveSheet.Names("table1").Address

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

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

或者:

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

Range("iterationtable")有时可行的原因(即,ActiveSheet是否有充分记录,且标准不正确的标准符号的正常预期功能:Range指< em>总是到运行时 Active 的任何表格,除非明确确定其他范围。

This是关于如何避免臭名昭着的1004错误的一个很好的入门书,但它归结为上述:适当地调整对象范围,理想情况下使用变量来表示它们。< / p>

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

最后:

  

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

好的,所以如果用户从&#34;背景表&#34;更改工作表名称除此之外,上面的代码仍然会失败。有一些方法可以防止这种情况,例如锁定工作表进行编辑和/或隐藏工作表(假设用户不需要输入数据到工作表等) ,或通过其CodeName而不是Name引用工作表。这利用了when referred to by CodeName, the Worksheet is always implicitly a part of ThisWorkbook这一事实。要查找工作表CodeName,请在“项目”窗格的括号中找到:

enter image description here

在上面的解决方案中,您将更改此行:

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

要:

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

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

答案 1 :(得分:0)

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

尝试将调用更改为以下格式:

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

你改变的地方&#34; CorrectWorkbookName&#34;到具有命名范围的书的名称并更改&#34; CorrectWorksheetName&#34;到包含命名范围的工作表的名称。

答案 2 :(得分:0)

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

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