检查VBA中文本框是否存在(使用名称)

5

我正在使用Ms-Access,创建了一个用户窗体,其中包含多个文本框。这些文本框的名称为:Box1,Box2,Box3 ...

我需要循环遍历所有文本框,但我不知道哪一个是最后一个。为了避免循环遍历所有用户窗体控件,我想尝试以下方法:

For i =1 To 20

If Me.Controls("Box" & i).value = MyCondition Then
    'do stuff
End If

Next i

这个错误发生在Box6,这是第一个未找到的框。有没有一种方法可以捕获此错误并在发生时退出循环。
我知道我可以使用On Error,但我更愿意使用代码捕获这个特定实例。
谢谢, 乔治

2
我很惊讶那个“Conrtols”对象上的任何迭代都能工作:P - SierraOscar
1
@MacroMan - 这很可能是用户定义的 :} - Vityata
2
这不是在Access中使用表单的一种非常糟糕的方式吗?您正在寻找最后一个控件,这意味着您正在运行时向表单添加文本框,我一直认为这是一个坏主意(在Access中),因为您需要在设计模式下打开表单才能添加控件,添加代码以便您可以使用按钮(而不是连接到控件的类)-这个问题难道不是在指出表单设计可能更好,或者可能有更好的解决方案吗? - Darren Bartrup-Cook
@DarrenBartrup-Cook - 我需要许多表单具有类似的初始设计,从同一位置提取数据。但是在如何分析这些数据方面却非常不同。每个框将采用用户定义的标识符。因此,这是一个模块的一部分,将用于许多表单。不同表单上的框的数量可以在5到15之间变化。因此,我想给它们相同的名称(实际实现中不会是Box)。我想我可以在所有表单上始终拥有20个框并隐藏不活动的框,但我宁愿不这样做。Me.Controls 最终也将需要更改,但目前还不相关。 - George
1
如果表单设计足够相似,我可能会使用一个带有20个框的单一表单,并编写所有分析代码以仅在该表单上工作,并使用某种标识符告诉它执行哪些分析。因此,大部分代码实际上不会位于表单中,而是位于从表单调用的普通模块中。或者采用交换机样式的表单-在连续表单上放置一个单一按钮,连接到一个包含按钮文本和参数的表格。 - Darren Bartrup-Cook
4个回答

5
一个 控件集合 是一组简单的控件,它们有一个相同的顺序,与控件的放置顺序相同。
首先,即使是可创建的集合对象也缺乏ExistsContains等方法,因此您需要一个带有错误处理函数来检查/从集合中提取小部件。
Public Function ExistsWidget(ByVal Name As String) As Boolean
    On Error Resume Next
        ExistsWidget = Not Me.Controls(Name) Is Nothing
    On Error GoTo 0
End Function

如果您真的不喜欢"先行为,后征得许可"选项,您可以拉取整个文本框的有序集合(并/或通过名称在另一个类似逻辑的循环中检查其存在性)。
Public Function PullBoxes() As Collection
    Dim Control As MSForms.Control

    Set PullBoxes = New Collection

    For Each Control In Me.Controls
        If TypeOf Control Is MSForms.TextBox And _
                Left(Control.Name, 3) = "Box" Then
                Call PullBoxes.Add(Control)
        End If
    Next
End Function

由于小部件的名称是唯一的,因此您可以从该函数返回一个带有(Control.Name、Control)对的Dictionary,并能够正确地检查小部件的名称是否存在,而不需要错误抑制。
如果 Dictionary 对您来说是新信息,则这里有一份很好的指南。
无论选择哪个对象,如果用户(或代码)无法创建更多的文本框,则可以将此上述Function转换为具有Static集合的Static Property Get或只是Property Get,这样您只需要在UserForm_Initialize事件中迭代所有控件一次!
Public Property Get Boxes() As Collection
    Static PreservedBoxes As Collection

    'There's no loop, but call to PullBoxes to reduce duplicate code in answer
    If PreservedBoxes Is Nothing Then _
            Set PreservedBoxes = PullBoxes

    Set Boxes = PreservedBoxes
End Property

毕竟,最后创建的名称为Box*TextBox将会是:

Public Function LastCreatedBox() As MSForms.TextBox
    Dim Boxes As Collection

    Set Boxes = PullBoxes

    With Boxes
        If .Count <> 0 Then _
                Set LastCreatedBox = Boxes(.Count)
    End With
End Function

我认为现在对你来说事情更清晰了!干杯!
注:所有代码都是表单的一堆方法/属性,因此所有内容都应放置在表单模块内。

在初始化表单时循环遍历所有控件是一个好主意。你添加了很多有助于学习的信息。但是为什么不(为此目的)通过只添加MyBoxes = MyBoxes + 1来缩写所有内容,而不是Call PullBoxes.Add(Control)MyBoxes可以是一个公共变量或者是表单上隐藏的标签。 - George
@George,抱歉,请问MyBoxes变量是什么?一个代表计数器的Long变量吗?如果是这样,那么您可以检查集合/字典的Count属性。如果您要创建一个公共集合而不是属性-那就由您决定,在这两种情况下行为没有区别(但取决于您将放置公共变量的位置)。如果它是某种迭代器/当前索引-那么请详细说明您需要它的原因是什么? - CommonSense
我在询问是否将Public Function PullBoxes() as collection替换为Public Function MyBoxes() as integer更好。 MyBoxes将在初始化表单时运行并计算我拥有的盒子数量。缩短所有内容到for each control in me.controls循环中。 Existswidgetgetboxeslastcreatedbox将不再需要。 - George

2
长话短说 - 你不能用VBA做你想做的事情。 然而,有一个好的方法可以绕过它 - 制作一个布尔公式,使用On Error检查对象是否存在。这样,你的代码就不会被破坏了。
Function ControlExists(ControlName As String, FormCheck As Form) As Boolean
   Dim strTest As String
   On Error Resume Next
   strTest = FormCheck(ControlName).Name
   ControlExists = (Err.Number = 0)
End Function

源自这里:http://www.tek-tips.com/viewthread.cfm?qid=1029435

要查看整个代码的运行情况,请按照以下方式检查:

Option Explicit

Sub TestMe()

    Dim i       As Long

    For i = 1 To 20
        If fnBlnExists("Label" & i, UserForm1) Then
            Debug.Print UserForm1.Controls(CStr("Label" & i)).Name & " EXISTS"
        Else
            Debug.Print "Does Not exist!"
        End If
    Next i

End Sub

Public Function fnBlnExists(ControlName As String, ByRef FormCheck As UserForm) As Boolean

    Dim strTest As String
    On Error Resume Next
    strTest = FormCheck(ControlName).Name
    fnBlnExists = (Err.Number = 0)

End Function

分离函数可能是一个答案。并且使用 Else Exit For。看起来要么这样做,要么在初始化表单时循环所有控件并获取循环的结束值。 - George
@George - 代码的目的是展示 fnBlnExists 如何工作的。因此,我加了 debug.print - Vityata

0
我建议按照以下方式在另一个过程中测试存在性:-
Private Sub Command1_Click()
Dim i As Long

i = 1
Do Until Not BoxExists(i)
    If Me.Conrtols("Box" & i).Value = MyCondition Then
        'Do stuff
    End If
    i = i + 1
Next
End Sub

Private Function BoxExists(ByVal LngID As Long) As Boolean
Dim Ctrl As Control

On Error GoTo ErrorHandle

Set Ctrl = Me.Controls("BoX" & LngID)
Set Ctrl = Nothing

BoxExists = True

Exit Function
ErrorHandle:
Err.Clear
End Function

在上述代码中,只有当盒子确实存在时,BoxExists才会返回true。

0

您在这里采取了错误的方法。

如果您想限制循环,可以仅在控件所在的区域(例如详细信息)中进行循环。您可以使用ControlType属性将控件限制为文本框。

Dim ctl As Control
For Each ctl In Me.Detail.Controls
    If ctl.ControlType = acTextBox Then
        If ctl.Value = MyCondition Then
            'do stuff
        End If
    End If
Next ctl

我认为通过循环来检查控件名称是否存在会比使用帮助函数和On Error Resume Next更快。

但这只是个人观点。


Geia sou Kosta :). 感谢您的回答。确实有效,但它会循环遍历所有控件。我还需要在那里添加一个额外的条件,以便 if ctl.controltype = acTextBox and LEFT(ctl.name, 3) = "Box" Then。这就是为什么我只想循环遍历名为 Box1、Box2 等的控件。 - George
你好,乔治 :). 我不禁要问一下,为什么你只需要循环遍历名为“Box”的文本框。这个表单有多少控件? - Kostas K.
没有那么多必要。我目前正在循环遍历所有控件,没有注意到延迟。但我是新手程序员,我正在努力养成高效工作的习惯。所以回答你的问题:a)我想知道是否可能并学习如何做;b)我想养成至少考虑更高效方法的习惯,这样我至少可以注释我的代码,并在以后的阶段进行修订。请记住,通过提问,我还得到了关于更好的表单设计和集合作为替代方法的有用评论。 - George

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接