MS Access 以编程方式引发表单事件

Posted

技术标签:

【中文标题】MS Access 以编程方式引发表单事件【英文标题】:MS Access raise form events programmatically 【发布时间】:2010-05-10 17:09:40 【问题描述】:

是否可以通过编程方式引发内置的 MS Access 表单事件?我有一个 感觉不是,但我想我会检查一下。 (我使用的是 Access 2003)。

例如,我想在 形式:

RaiseEvent Delete(Cancel)

并让它触发 Access.Form 删除事件——即实际上没有 删除绑定记录。

请注意,我的删除事件不是由表单本身处理,而是由外部 类,所以我不能简单地调用 Form_Delete(Cancel)。

【问题讨论】:

不是一个答案,但即使可能,我也建议不要这样做,因为这听起来很难为未来的开发人员记录和维护。不能直接调用你的外部类或者修改一下直接调用吗? 如果您的“删除事件不是由表单本身而是由外部类处理”,那么它不是表单事件,是吗?我无法理解。 这是一个描述你失败的解决方案而不是实际问题的典型案例。该解决方案是相对初级的 Access 编程,但是您跳到了一个非常先进的可能解决方案,它不可能工作。通过不解释你真正的问题是什么,你排除了任何获得可用答案的机会(直到你告诉我们你真正想要做什么)。 【参考方案1】:

我能理解你的困惑——我没有解释任何更大的背景。对不起。

基本上,情况是我有一个“索引”,即绑定到只读查询的“连续形式”表单。该查询必须是只读的,因为它涉及外部连接。但是,我希望能够从此表单中删除基础记录。

所以我的第一个想法是在表单记录集之外进行删除,例如。使用删除查询。我希望通过自己引发这些事件来围绕这个手动删除例程挂钩标准的 Delete/BeforeDelConfirm/AfterDelConfirm 事件。但很可惜,这是不可能的。

如果表单本身处理了这些事件,我可以简单地调用处理程序(Form_Delete 等),但我的项目有自定义类来处理所有表单的表单删除和更新事件(验证、确认、日志记录等) . (@Smandoli,它没有很好的文档,我几个月前才发现它并且现在广泛使用它——也许你已经知道了——你可以设置外部类来处理你的表单事件。参见例如 here)

长话短说,我找到了令我满意的解决方法。它涉及使“索引”表单成为另一个表单的子表单,该表单绑定到可以从中删除的记录集。因此,可以根据内部表单中的选择,使用标准 Access 表单事件在外部表单中完成删除。

@Knox,我原则上不同意自己提出“内置”事件很难记录和维护。许多其他框架都依赖于它。 在实践中,我同意你的看法,因为我们都必须在我们的工具和围绕这些限制发展的“最佳实践”的限制范围内工作。 Access 的优点和缺点在于它在记录集和表单之间的紧密结合......

【讨论】:

我很困惑。这是沼泽标准的 Access 设计——您将 DELETE 命令按钮放在连续表单的页眉或页脚中,然后从那里运行删除代码,然后执行 Me.Requery。这里一点也不复杂,也不需要它是父形式。 如果“运行删除代码”是指运行删除查询或使用 DAO,即在表单记录集之外,那是我的第一个想法,我在上面解释了为什么这不起作用对我来说:因为我想加入标准表单事件。然而,因为表单记录集是只读的,所以我不能简单地使用 DoCmd.RunCommand acCmdDeleteRecord。我认为我的解决方法很优雅,并且很高兴与任何想要它的人分享。【参考方案2】:

通常,您根本不需要自己触发标准表单事件,通常这表明对一般表单事件的错误理解(如果甚至不是一般事件)。

表单事件的存在是为了对用户与表单的交互做出反应,或者通知代码背后的代码通常会发生一些事情(例如 Form_Load 事件)。事件潜艇可以对这些事件做出反应 - 仅此而已。

人们想直接执行事件subs是很常见的事情,但这也是一种错误的方式。事件 subs 通常被声明为“Private”而不是“Public”是有原因的,它应该防止直接从代码模块外部调用它们,但实际上你也不应该在同一个代码模块中执行它们中的任何一个。事件 subs 总是必须由它们的事件专门调用,尽管可以直接调用它们。

如果事件子程序有任何代码也应该在其他地方执行,则在同一模块内创建一个私有或公共子程序(取决于您是否要从外部执行它们)然后从事件子程序调用此子程序.如果您认为必须从其他地方执行相同的子程序,您现在也可以调用相同的子程序。这不是“可以直接调用事件sub”的问题,主要是设计问题。您应该始终确保事件子仅由事件本身调用,而不是由任何代码调用。通过代码调用事件子时的问题是,如果您执行代码并且真实事件执行它,您可能会很快遇到麻烦。最后你会得到一大堆代码,很难调试。

顺便说一句,当然可以从引用表单的类模块调用事件 subs(如果您使用类模块来处理一般事件,则需要这样做)。您只需要将事件 subs 声明为 Public,然后您可以使用表单引用调用它们,但如上所述:不要那样做。

    如果使用类模块来处理事件,那么您可以在这里做任何事情,您不需要表单代码。 如果查询是只读的,并且您想删除基表的记录,则没有事件子可以帮助您。当用户想要删除他不能做的事情时,他们会被解雇,因为它是只读的,所以 DoCmd 也不能帮助你。 就像大卫在上面的评论中所说,只需在您想要的任何位置创建一个删除按钮,然后可以在连续表单中读出当前行的 ID 并启动“DELETE”SQL 命令,然后只需重新查询连续表单并你完成了。您也可以在标准类模块中处理此问题,因为您不仅可以转发表单事件,还可以以相同的方式转发控制事件。在您的类模块中创建一个 Init 过程,该过程从您想要处理的表单中获取所有控件,任何可能附加的每个连续表单中的基表名称,然后类模块可以将其分配给定义的标准“WithEvents”例如 CommandButton 类型的控制变量并将基表名称保存到字符串变量。 (不要忘记在 Init 过程中将 OnClick 事件设置为“[Event Procedure]”。) 在您可能初始化类模块的连续表单的 Load 事件中,您可以将基表名称和删除按钮控件转发到类模块的 Init 过程,然后可以通过启动一个非常通用的方式处理删除DELETE 查询基表并重新查询表单,因为它也已经具有表单引用。无需调用任何事件过程。

最后但同样重要的是:也许有些框架允许您直接引发事件,但通常我会说此类框架的创建者也不了解事件过程的目的。如果您曾经在自己的类模块中创建过自己的事件,您会发现它们也不能在类模块之外引发。当然,您可以自己创建一个“RaiseEvent”子以在外部调用它们——事实上,如果是自己的事件,它在某些情况下是有意义的。在表单(控制...)事件的情况下,他们应该通知代码发生了什么事,现在应该有反应。如果您在自己的类模块中使用事件,您通常还会在外部模块中创建一个“WithEvents”变量,以便在其他类模块中发生事件时获得通知。一个事件应该可以使模块对象彼此独立。带有事件的模块只会引发事件,它不知道是否有人在听它或对此事件做出反应。它通知“世界”在类模块中发生了一些事情,没有别的。就像一个广播电台,它向世界发送每日新闻,但它不知道是否有人收听。通常,广播电台的听众不会去广播电台为其他听众阅读他自己的新闻。只有广播电台的人才能决定发送什么以及何时发送。同样的故事。

【讨论】:

以上是关于MS Access 以编程方式引发表单事件的主要内容,如果未能解决你的问题,请参考以下文章

MS Access 2010 - 如何根据以前的输入以编程方式显示表单字段?

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

运行时错误 3032,无法执行此操作 ms-access

MS Access“更新后”事件:引发错误

MS Access 2007 重命名自定义组中的快捷方式以更改对象的名称

专家 - 表单打开方式不同于设计视图与 MS Access 对象列表