VBA错误处理仅适用于第一次传递

时间:2011-09-08 00:10:51

标签: excel vba

我的代码是:

Sub zaa()
'code to find another open file that has the defined name "data" 
'    - useful where the name changes each month or week

For Each wb In Workbooks
  On Error GoTo abcd
  x = wb.Name
  Workbooks(x).Activate
  If Range("Data").Address <> "" Then y = wb.Name

  Exit Sub

abcd:
Next wb

End Sub

当我知道具有特定命名范围的Excel文件存在但却不知道每周或每月更改的文件名时,基本目标。目标是找到文件并在那时退出sub(或者然后在该文件上执行其他代码并退出而不是转到其他文件。)

我觉得如果我只打开两个文件就可以了,但如果有更多文件则没有(除非目标一个是第二个)。虽然我可以用我所拥有的东西运行,但我认为其他人可能会从我拥有的东西中获益,而且我可以拥有更强大的解决方案。

更新: 感谢所有响应过的人。 Mitch将原始帖子放入可读格式! (我已经学会了如何纠正这个问题,并能够直接复制代码 - 我在下面的一些评论中指出我遇到了麻烦。) 我有不同程度的成功 - 来自PaulStock&amp; Reafidy最初都为我工作,但PaulStock代码已停止为我工作。回应&amp;来自Jean-FrançoisCorbett和Chris Neilsen的代码对我很有帮助,但是现在当我运行代码时(原样)它似乎什么都不做 - 也就是说不会离开我运行它的表单(并且这不是范围名称的位置)数据出现)。我最初在Excel 2007中运行与其他宏的工作表中的代码,但尝试获得不同的结果已将它们移动到一个单独的工作表,没有打开其他宏文件。还尝试从保存为'97 -'03格式的文件中运行它们。两者都没有产生不同的结果。其他人比我有更多的经验(看到我在与Reafidy的评论讨论中证明的错误,并记住我的原始发布的代码是通过谷歌找到的材料的结果,并由我修改为特定的任务和应用程序和暗示,我是不够精明,不能自己想出来)可能会找到更好的解决方案但是现在:

当Reafidy的代码适合我时,我感到非常高兴。

由于我没有注册(我确实尝试但是没有用)并且没有足够的声望点我不能投票但是我在Reafidy的解决方案旁边放了一个复选标记。

进一步更新: 我现在发现了PaulStock&amp; Jean-FrançoisCorbett的代码对我不起作用,因为我的差异非常小 - 代码包含“数据”,而我的命名范围是“数据”。在他们的代码中进行这种更改(以允许区分大小写)意味着他们的两个解决方案现在都适用于我,因此我试图在他们的解决方案中添加一个勾号!不幸的是,我发现只有一个解决方案可以打勾。

我还了解到,我太过于字面意思地使用了克里斯的代码。努力测试每个代码“按原样”,这就是我所做的。在'做东西'的部分中,“wb.activate”的一个简单的附加元素使得代码能够满足我的需求。

再次感谢所有四项贡献。

4 个答案:

答案 0 :(得分:4)

你在代码中做了一些令人费解的事情,我认为这是令人困惑的问题。

让你的错误处理程序(abcd:)在For...Next循环的中间开始是非常糟糕的做法,只会导致混乱。我想不出为什么要这样做的原因。

无论如何,没有必要为此任务使用错误处理。我知道有一些例外情况,糟糕的VBA设计迫使我们使用错误处理而不是内置的VBA功能(例如测试是否为非Variant array has been allocated),但这不是其中一种情况。有一种标准的方法(.Names工作簿对象的集合),并使用错误处理而不是这是混乱和复杂,并要求麻烦。

另外,为什么说

x = wb.Name
Workbooks(x).Activate

什么时候可以说wb.Activate?您没有使用x进行任何操作。或者y,就此而言。

相对于原始代码以及到目前为止提供的其他答案,以下工作简化和优化:

Sub zaa2()
    Dim wb As Workbook
    Dim nm As Name

    For Each wb In Workbooks
        For Each nm In wb.Names
           If nm.Name = "Data" Then
                wb.Activate
                Exit Sub
           End If
        Next
    Next wb
End Sub
' A workbook containing a range named "Data" is now activated 
' (if one is found amongst the open workbooks). 
' Note that there may be more than one, but only the first found is activated...

编辑:在评论中,您提到由于大写"Data"和小写"data"之间的混淆而导致您遇到麻烦。为了防止将来出现这种情况,一种可能性是忽视案件。这可以按如下方式完成:

If StrComp(nm.Name, "data", vbTextCompare) = 0 Then

If LCase(nm.Name) = "data" Then

如果Truenm.Name"Data""data"等,两者都会返回"dATa"

答案 1 :(得分:1)

你不能像这样使用错误处理。要么将错误处理移出循环,要么每次都将错误处理程序重置,所以请使用如下错误处理:

更受欢迎的替代方案是:

Sub Test()

    For Each wb In Workbooks
        x = wb.Name
        Workbooks(x).Activate            

        If RangeExists("Data") Then
            y = wb.Name
            Exit Sub
        End If

    Next wb

End Sub
Function RangeExists(s As String) As Boolean
    On Error Resume Next
    RangeExists = Range(s).Count > 0
End Function

修改

@ Jean-FrançoisCorbett,我不得不说你快速跳下投票按钮。我发布的第一个解决方案是因为我假设OP没有发布他的整个代码,因此我没有尝试简化它或像我通常那样“清理它”。我同意我没有很好地说出我的答案,但关于第一个解决方案,我试图证明他需要重置错误处理程序。不幸的是,我应该说“首选的替代方案”。

@Derek,抱歉,我无法及时回答您的问题。显然你可以自由选择你喜欢的任何方法。在我看来,其他人提供的多循环解决方案深入到工作簿名称集合中是不必要的,并且长篇大论。现在更重要的是,名称集合可以包含引用常量,公式或范围的名称。我假设您只想检查定义的名称是否特别是命名范围,这意味着其他人提供的循环方法需要调整为可靠。

我同意其他人的意见,即应该避免错误处理但是excel中不必要的循环可能与使用错误处理一样邪恶,而且我个人就像瘟疫一样避免它。

上面的函数可以放在自己的模块中,并可以根据需要重复使用。它快速,可靠,避免不必要的循环,特定检查工作簿中的命名范围,并且是最广泛接受/使用的方法,用于检查excel vba社区中是否存在命名范围(我的意思是使用函数和错误通过名称集合处理循环)。如果你不相信我,谷歌搜索“检查命名范围是否存在”。或者在www.ozgrid.com/forum询问是否需要其他excel vba专家意见。

现在我知道您已经发布了整个代码并且您不打算激活每个工作簿,您可以使用此代码来激活使用命名范围“data”找到的第一个工作簿:

Sub Test3()
    Dim wbLoop As Workbook

    For Each wbLoop In Workbooks
        If RangeExists("data", wbLoop) Then
            wbLoop.Activate
            Exit Sub
        End If
    Next wbLoop

End Sub
Function RangeExists(s As String, wb As Workbook) As Boolean
   On Error Resume Next
   RangeExists = wb.Names(s).RefersToRange.Count > 0
End Function

我完全理解积极批评的必要性,如果使用得当,我相信投票系统。然而,我认为这是一个合理的解决方案以及我对操作格式问题的帮助,有两个下来的选票 - 不幸的是我无法帮助,但我觉得我想要远离这个论坛。

答案 2 :(得分:1)

这是一种替代方法,而不会使用错误处理程序

Sub zaa()
    Dim wb As Workbook
    Dim CheckForNamedRange As Boolean
    Dim nm As Name

    On Error GoTo EH

    For Each wb In Workbooks
        CheckForNamedRange = True
        Set nm = wb.Names("data")
        If CheckForNamedRange Then
            ' Name found
            ' do stuff

            Exit For
        End If
    Next
Exit Sub
EH:
    If CheckForNamedRange Then
        ' Name not found
        Err.Clear
        CheckForNamedRange = False
        Resume Next
    Else
        ' Some other error occured, so handle it
        '...
    End If
End Sub

答案 3 :(得分:1)

试试这段代码。不必担心会出错。

Sub zaa()
    For Each wb In Workbooks
        x = wb.Name
        Workbooks(x).Activate
        For Each n In Workbooks(x).Names
           If n.Name = "Data" Then
                y = wb.Name
                Exit Sub
           End If
        Next

    Next wb
End Sub