VBA:如果用户删除单元格,Range对象会发生什么?

Joh*_*hnB 29 excel vba excel-vba

假设我在有一些带有一些r类型变量的模块Range.假设在某些时候,我在那里存储一个Range对象(例如活动单元格).现在我的问题是:r如果用户删除单元格(单元格,而不仅是其值),会发生什么?

我试图在VBA中解决这个问题,但没有成功.结果很奇怪.r不是Nothing,r报告的值是类型Range,但如果我尝试在调试器窗口中查看其属性,则每个属性的值都报告为"对象需要".

如何以编程方式确定变量r是否处于此状态?

我可以这样做而不会产生错误并捕获它吗?

Dou*_*ncy 22

好问题!我以前从未考虑过这个问题,但是我认为这个函数会识别一个被初始化的范围 - 不是Nothing - 但是现在处于"Object Required"状态,因为它的单元格被删除了:

Function RangeWasDeclaredAndEntirelyDeleted(r As Range) As Boolean
Dim TestAddress As String

If r Is Nothing Then
    Exit Function
End If
On Error Resume Next
TestAddress = r.Address
If Err.Number = 424 Then    'object required
    RangeWasDeclaredAndEntirelyDeleted = True
End If
End Function
Run Code Online (Sandbox Code Playgroud)

你可以测试是这样的:

Sub test()
Dim r As Range

Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
Set r = ActiveSheet.Range("A1")
Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
r.EntireRow.Delete
Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
End Sub
Run Code Online (Sandbox Code Playgroud)


Jos*_*eph 6

我相信,当您使用VBA设置关键字,它会在后台工作表的Range对象在您指定的工作表的指针(每一单元的工作表的单元格的集合一个给定范围的对象).当您仍在内存中引用范围时删除范围时,已取消分配Range变量指向的对象的内存.

然而,你的范围变量最可能仍包含指向最近移除Range对象,这就是为什么它也并非一无是处,但无论它指向不存在了,这会导致问题,当您再次尝试使用变量.

看看这段代码,看看我的意思:

Public Sub test2()
    Dim r As Excel.Range
    Debug.Print ObjPtr(r)           ' 0

    Set r = ActiveSheet.Range("A1")
    Debug.Print ObjPtr(r)           ' some address

    r.Value = "Hello"

    r.Delete
    Debug.Print ObjPtr(r)           ' same address as before
End Sub
Run Code Online (Sandbox Code Playgroud)

有关ObjPtr()的更多信息,请查看此文章:http: //support.microsoft.com/kb/199​​824

因此,虽然您有一个对象的有效地址,但遗憾的是该对象不再存在,因为它已被删除.看来"Is Nothing"只是检查指针中的地址(我认为VBA认为该变量是"Set").

至于如何解决这个问题,不幸的是我现在没有看到干净的方式(如果有人找到一个优雅的方式处理这个,请发布它!).您可以使用On Error Resume Next,如下所示:

Public Sub test3()
    Dim r As Excel.Range
    Debug.Print ObjPtr(r)           ' 0

    Set r = ActiveSheet.Range("A1")
    Debug.Print ObjPtr(r)           ' some address

    r.Value = "Hello"

    r.Delete
    Debug.Print ObjPtr(r)           ' same address as before

    On Error Resume Next
    Debug.Print r.Value
    If (Err.Number <> 0) Then
        Debug.Print "We have a problem here..."; Err.Number; Err.Description
    End If
    On Error GoTo 0
End Sub
Run Code Online (Sandbox Code Playgroud)