VBA 中的虚拟范围对象
Virtual Range object in VBA
我有一个宏,我试图用它来爬行工作簿以查找特定用户定义函数 (UDF) 的实例,并检查它找到的每个 UDF 的参数是否满足条件.
根本问题是如何获取 Range 对象(单个单元格)并将 UDF 的参数解析为实际值,即不只是读取 .Formula 字符串,它可能包含实际值和单元格引用的混合,但要识别实际上是单元格引用的参数并解决这些问题。如果我手动执行此操作,则有点像选择 UDF 的每个参数并按 F9 进行计算,是否有意义?
我愿意接受任何稳健的方法来实现最终目标,正如我所说,最终目标是能够解析传递给 UDF 的参数的实际值,显然不是来自 UDF 代码本身。
具体来说,我使用以下方法遍历工作簿中的所有工作表:
For Each ws in ActiveWorkbook.Worksheets
然后我使用搜索功能(超出本期讨论范围)return 一组使用我的 UDF 的单元格,我将其称为
UDF(arg1, arg2)
为了保持简单。
然后我遍历该单元格集合,对于每个单元格,我想基本上将参数连接成一个以“”分隔的字符串。因此,如果特定单元格包含公式
=UDF(A1, "test")
假设单元格 A1 包含“一月”,需要明确的是,我不希望对“A1\test”进行 return 编辑,而是对“January\test”进行编辑。
我想我已经解决了这个问题(可能还有其他更优雅的方法)。
我最终编写了一个辅助函数来尝试解析单元格引用(相同的 sheet、不同的 sheet、命名范围)并使用它(连同 special-character 修剪)来解析所有参数到我的 UDF。本质上它是一个解析器。
代码是(对于参数解析器 - 假设已经解析了 UDF .Formula 字符串以使用逗号分隔或其他方式提取参数):
Private Function resolveCellRef(cellRef As String, cell As Range) As String
' This function takes an argument to a UDF that might be a cell reference or Named Range, and tries to resolve the value.
' It needs to handle the cases where a value has been passed, and just return the value again.
' Created by Rob Baker, 11/02/2022
Dim pos1 As Integer, tmpStr As String
Dim ws As Worksheet
Dim error_raised As Boolean
' Check if it is already a string argument. If so, trim the quotation marks and return
If Left(cellRef, 1) = """" Then
resolveCellRef = Replace(cellRef, """", vbNullString)
Exit Function
End If
' Check for whether the argument is a cell reference to a foreign sheet
error_raised = False
pos1 = InStr(1, cellRef, "!")
Select Case pos1
Case 0
Set ws = cell.Parent ' Cell reference must be local to sheet
tmpStr = cellRef
Case Else
tmpStr = Left(cellRef, pos1 - 1)
If Left(tmpStr, 1) = "'" Then ' Trim apostrophes if present
tmpStr = Left(tmpStr, Len(tmpStr) - 1)
tmpStr = Right(tmpStr, Len(tmpStr) - 1)
End If
Set ws = ActiveWorkbook.Worksheets(tmpStr) ' Set the ws reference
tmpStr = Right(cellRef, Len(cellRef) - pos1) ' This is now the cell reference alone
End Select
On Error GoTo errhandler ' Use error handling to handle two known issues
resolveCellRef = ws.Range(tmpStr).Value2
If error_raised Then resolveCellRef = ActiveWorkbook.Names(cellRef).RefersToRange.Value2
Exit Function
errhandler:
If Err.Number = 1004 And Not error_raised Then ' Set the error_raised flag and resume next to try a Named Range
Err.Clear: error_raised = True
Resume Next
ElseIf Err.Number = 1004 And error_raised Then ' If we get here, then the cellRef isn't a cell reference or Named Range, so just return it as is
Err.Clear
resolveCellRef = cellRef
Resume Next
End If
End Function
我修改了上面复杂的(但主要是功能性的)答案,使用 Worksheet.Evaluate:
更简单的形式
Private Function resolveCellRef(CellRef As String, cell As Range) As String
' This function takes an argument to a UDF that might be a cell reference or Named Range, and tries to resolve the value.
' It needs to handle the cases where a value has been passed, and just return the value again.
' RMB v1.0.0: whole overhaul of how this function works
Dim ws As Worksheet
Set ws = cell.Parent
resolveCellRef = ws.Evaluate(CellRef)
End Function
当我意识到这个 Worksheet 方法存在时,真是一个 'well duh' 时刻!
我花了相当多的精力使参数解析器稳健地工作,这实际上非常简单,因为使用 Evaluate 的工作表版本(而不是同名的 Application 方法)自动继承单元格引用、命名范围等的上下文
我有一个宏,我试图用它来爬行工作簿以查找特定用户定义函数 (UDF) 的实例,并检查它找到的每个 UDF 的参数是否满足条件.
根本问题是如何获取 Range 对象(单个单元格)并将 UDF 的参数解析为实际值,即不只是读取 .Formula 字符串,它可能包含实际值和单元格引用的混合,但要识别实际上是单元格引用的参数并解决这些问题。如果我手动执行此操作,则有点像选择 UDF 的每个参数并按 F9 进行计算,是否有意义?
我愿意接受任何稳健的方法来实现最终目标,正如我所说,最终目标是能够解析传递给 UDF 的参数的实际值,显然不是来自 UDF 代码本身。
具体来说,我使用以下方法遍历工作簿中的所有工作表:
For Each ws in ActiveWorkbook.Worksheets
然后我使用搜索功能(超出本期讨论范围)return 一组使用我的 UDF 的单元格,我将其称为
UDF(arg1, arg2)
为了保持简单。
然后我遍历该单元格集合,对于每个单元格,我想基本上将参数连接成一个以“”分隔的字符串。因此,如果特定单元格包含公式
=UDF(A1, "test")
假设单元格 A1 包含“一月”,需要明确的是,我不希望对“A1\test”进行 return 编辑,而是对“January\test”进行编辑。
我想我已经解决了这个问题(可能还有其他更优雅的方法)。 我最终编写了一个辅助函数来尝试解析单元格引用(相同的 sheet、不同的 sheet、命名范围)并使用它(连同 special-character 修剪)来解析所有参数到我的 UDF。本质上它是一个解析器。
代码是(对于参数解析器 - 假设已经解析了 UDF .Formula 字符串以使用逗号分隔或其他方式提取参数):
Private Function resolveCellRef(cellRef As String, cell As Range) As String
' This function takes an argument to a UDF that might be a cell reference or Named Range, and tries to resolve the value.
' It needs to handle the cases where a value has been passed, and just return the value again.
' Created by Rob Baker, 11/02/2022
Dim pos1 As Integer, tmpStr As String
Dim ws As Worksheet
Dim error_raised As Boolean
' Check if it is already a string argument. If so, trim the quotation marks and return
If Left(cellRef, 1) = """" Then
resolveCellRef = Replace(cellRef, """", vbNullString)
Exit Function
End If
' Check for whether the argument is a cell reference to a foreign sheet
error_raised = False
pos1 = InStr(1, cellRef, "!")
Select Case pos1
Case 0
Set ws = cell.Parent ' Cell reference must be local to sheet
tmpStr = cellRef
Case Else
tmpStr = Left(cellRef, pos1 - 1)
If Left(tmpStr, 1) = "'" Then ' Trim apostrophes if present
tmpStr = Left(tmpStr, Len(tmpStr) - 1)
tmpStr = Right(tmpStr, Len(tmpStr) - 1)
End If
Set ws = ActiveWorkbook.Worksheets(tmpStr) ' Set the ws reference
tmpStr = Right(cellRef, Len(cellRef) - pos1) ' This is now the cell reference alone
End Select
On Error GoTo errhandler ' Use error handling to handle two known issues
resolveCellRef = ws.Range(tmpStr).Value2
If error_raised Then resolveCellRef = ActiveWorkbook.Names(cellRef).RefersToRange.Value2
Exit Function
errhandler:
If Err.Number = 1004 And Not error_raised Then ' Set the error_raised flag and resume next to try a Named Range
Err.Clear: error_raised = True
Resume Next
ElseIf Err.Number = 1004 And error_raised Then ' If we get here, then the cellRef isn't a cell reference or Named Range, so just return it as is
Err.Clear
resolveCellRef = cellRef
Resume Next
End If
End Function
我修改了上面复杂的(但主要是功能性的)答案,使用 Worksheet.Evaluate:
更简单的形式Private Function resolveCellRef(CellRef As String, cell As Range) As String
' This function takes an argument to a UDF that might be a cell reference or Named Range, and tries to resolve the value.
' It needs to handle the cases where a value has been passed, and just return the value again.
' RMB v1.0.0: whole overhaul of how this function works
Dim ws As Worksheet
Set ws = cell.Parent
resolveCellRef = ws.Evaluate(CellRef)
End Function
当我意识到这个 Worksheet 方法存在时,真是一个 'well duh' 时刻! 我花了相当多的精力使参数解析器稳健地工作,这实际上非常简单,因为使用 Evaluate 的工作表版本(而不是同名的 Application 方法)自动继承单元格引用、命名范围等的上下文