有没有办法在 VBA(MS Access 2002 或 2003)中调用任何控件的更新前事件过程?

Posted

技术标签:

【中文标题】有没有办法在 VBA(MS Access 2002 或 2003)中调用任何控件的更新前事件过程?【英文标题】:Is there a way to call the BeforeUpdate event procedure of any control in VBA (MS Access 2002 or 2003)? 【发布时间】:2009-05-28 17:16:39 【问题描述】:

在 VBA 中,我正在更改 Access 表单中一些控件的值。我喜欢在这样做之后运行这些控件的 BeforeUpdate 事件,因为它会检查字段之间的一致性:

Private Sub ExampleProc1()
  Dim intCancel as Integer

  intCancel = False

  Me.Controls("Date1").Value=Null
  Me.Controls("Textfield1").Value=Null

  Call Date1_BeforeUpdate(intCancel)
  Call Textfield1_BeforeUpdate(intCancel)
End Sub

我想让它通用,但我找不到运行事件过程的方法。我想要这样的东西:

Private Sub ExampleProc2(ParamArray Fields())
  Dim intCancel as Integer, varV as Variant

  For Each varV in Fields
    Me.Controls(varV).value=Null
    intCancel = False
    Call Me.Controls(varV).BeforeUpdate(intCancel)
  Next
End Sub

这甚至可能吗?也许 DoCmd.RunMacro 是要走的路?

【问题讨论】:

【参考方案1】:

其实你可以使用 CallByName 来做到这一点

CallByName Me, Me.Controls(varV).Name & "_BeforeUpdate", VbMethod, intCancel

【讨论】:

这正是我想要的。两点说明: - EventProcPrefix 属性似乎更合适:CallByName Me, Me.Controls(varV).EventProcPrefix & "_BeforeUpdate", VbMethod, intCancel - 被调用的方法需要是公共的,即使从表单对象内部调用 CallByName 也是如此。 【参考方案2】:

您可以考虑 Run (http://msdn.microsoft.com/en-us/library/aa199108(office.10).aspx) 和 Eval Function:

Debug.Print Eval("ShowNames()")    
Debug.Print Eval("StrComp(""Joe"",""joe"", 1)")
Debug.Print Eval("Date()")

【讨论】:

Run() 和 Eval() 特定于 Access。 Evaluate() 似乎是 Eval() 的 Excel 等价物。【参考方案3】:

您可能希望研究创建自定义集合,然后将它们传递给您的函数。我在表单中广泛使用它们来验证控件。假设您在表单的模块级别定义了此集合:

  Dim mcolDateFields As New Collection

然后在表单的 OnLoad 事件中,您可以这样做:

  mcolDateFields.Add Me!txtDateEntered, "txtDateEntered"
  mcolDateFields.Add Me!txtDatePrinted, "txtDatePrinted"
  mcolDateFields.Add Me!txtDateArchived, "txtDateArchived"

要遍历该集合,您需要执行以下操作:

  Dim varItem As Variant
  Dim ctl As Control

  For Each varItem In mcolDateFields
    Set ctl = varItem
    Debug.Print ctl.Name & ": " & ctl.Value
  Next varItem
  Set ctl = Nothing

...您可以将 Debug.Print 替换为实际有用的东西。

您还可以将控件传递给其他将集合作为参数的代码例程并对其进行处理:

  Public Sub (mcolCollection As Collection)
    Dim varItem As Variant
    Dim ctl As Control

    For Each varItem In mcolCollection 
      Set ctl = varItem
      Debug.Print ctl.Name & ": " & ctl.Value
    Next varItem
    Set ctl = Nothing
  End Sub

这可能允许您传递要验证的控件集合。由于您将控件引用存储在集合中,而不是控件名称或控件值,因此无需传递父表单名称。如果您需要它,您可以通过以下方式获得它:

  Dim varItem As Variant
  Dim ctl As Control

  For Each varItem In mcolCollection 
    Set ctl = varItem
    Debug.Print ctl.Parent.Name & "!" & ctl.Name & ": " & ctl.Value
  Next varItem
  Set ctl = Nothing

同样,如果您想以与在表单自己的模块中使用 Me 关键字相同的方式使用表单,只需使用 ctl.Parent,如果控件位于表单本身上,它将返回表单引用 (而不是,比如说,在选项卡控件上)。

您的代码示例中的一些 cmets - 您的第一个示例如下所示:

  Private Sub ExampleProc1()
    Dim intCancel as Integer

    intCancel = False

    Me.Controls("Date1").Value=Null
    Me.Controls("Textfield1").Value=Null

    Call Date1_BeforeUpdate(intCancel)
    Call Textfield1_BeforeUpdate(intCancel)
  End Sub

虽然在您创建可取消事件时它被定义为整数,但实际上根本不是 - 它是一个布尔值,要取消事件,您必须将其设置为 True (-1)。我认为这实际上是 Access VBA 在 VBA 之前的 Access 时代的保留,当时 Access Basic 缺少布尔数据类型。一般来说,我只对与 Cancel 参数一起使用的变量使用布尔值。

  Me.Controls("Date1").Value=Null

我不知道你为什么要这样做。我会这样编码:

  Me!Date1 = Null

您的代码过于冗长,因为您两次指定了默认值:Controls 集合是表单的默认集合。实际上,默认集合是 Controls 和 Fields 集合的串联,因此指定您要使用控件可能有充分的理由,例如,如果您正在使用相应字段缺少的控件属性。但是对于操作值,如果有一个与字段同名的控件,则使用哪个值并不重要,因为它们将是相同的,除非在极不可能的情况下,控件实际上并未绑定到该字段同名。

同样,.Value 是控件的默认属性,因此没有理由指定它。您可能想要这样做的唯一情况是,如果您将它用作调用另一个例程的参数,其中参数是 ByRef 而不是 ByVal。但一般来说,如果只是给控件赋值,就很浪费打字。

要完成您想要完成的工作,在我看来,控件引用的集合是一个很好的解决方案。不过,我不知道这些控件的事件是否需要公开。

【讨论】:

感谢您的解决方案和您的 cmets!我不知道取消类型,下次我会介意的。我指定 Controls 集合的原因是某些控件的名称中有空格。使用简短的语法,它们在 Access 中称为“Control One”,在 VBA 中称为“Control_One”:它使影响分析变得复杂。至于.Value,你自己写,除非有,否则没有理由指定它,所以指定它更容易!最后,我喜欢我的代码明确,这样对我(个人偏好)和年轻开发人员来说更易读。而且它没有缺点,不是吗? 我认为没有理由使用 .Value 除了强制评估控件。也就是说,如果您将控件引用作为参数传递给子例程/函数,如果它是 ByRef 而不是 ByVal,则传递 Me!MyControl 和 Me!MyControl.Value 之间存在差异,因为前者传递了对控制,而后者传递实际值。但这可以通过 (My!MyControl) 来完成,其中 parens 在传递给子之前强制评估。此外,sub 应该可能被编辑为 ByVal 而不是 ByRef。总之,不需要使用.Value。【参考方案4】:

显然可以在 Access 中执行 CallByName,但您也可以将所有控件的 BeforeUpdate 逻辑放在一个集中的子中,根据您传递的参数改变其行为。那么您在更新前事件处理程序中所拥有的只是对集中子程序的调用。

【讨论】:

以上是关于有没有办法在 VBA(MS Access 2002 或 2003)中调用任何控件的更新前事件过程?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法用 vba 在 MS-Access 中截屏?

MS Access vba中的分组字段

当字符串包含分号时,MS Access VBA AddItem

将 MS Access VBA 应用程序转换为 .NET Visual Studio 项目

MS Access 2003 - 有没有办法以编程方式定义图表的数据?

如何从同一数据库的 VBA 代码中的 MS ACCESS 中提取字段