Office中国论坛/Access中国论坛

标题: 【转载 / 文章】VBA对象级事件处理 / 【原创 / 文章】Access VBA中的事件处理 [打印本页]

作者: LucasLynn    时间: 2005-8-22 23:15
标题: 【转载 / 文章】VBA对象级事件处理 / 【原创 / 文章】Access VBA中的事件处理
、这篇文章虽然是针对AutoCAD环境而作,但是由于VBA的通用性,所以本文中描述的技术,在Access中完全可以使用,转贴以供VBA中高级开发者参考:

=====================================

对象级事件并不是一直存在于AutoCAD的VBA中的,也就是说,当一个VBA程序被装载时,对象级事件并不会自动被激活。对象级事件必须被VBA和所有其它的ActiveX 自动操作控制器激活。

一旦对象级事件被激活,你就有以下的事件可用:

Modified

当图中的某个对象被修改时引发。







本章主题

激活对象级事件



激活对象级事件

在你能使用对象级事件之前,你必须建立一个新的类模块,并声明一个对象为带事件的AcadObject类型。例如,假定一个新的类模块已经建立并命名为EventClassModule,这个新的类模块将包含带VBA关键字WithEvents的应用程序的声明。

建立一个新的类模块并声明一个带事件的Circle对象

1 在VBA IDE中,插入一个类模块。从插入菜单中选择类模块。

2 在工程窗口中选择新的类模块。

3 在工程窗口中将类模块的名称改为EventClassModule。

4 用F7或通过选择菜单项查看代码打开类模块的代码窗口。

5 在类模块的代码窗口中,增加以下行:

Public WithEvents Object As AcadCircle

当新的对象被声明是带事件的,它就出现在分类模块的对象下拉列表中,并且你可以在类模块中为新对象编写事件过程。(当你在对象框中选择了新对象,对这个对象有效的事件就列在过程下拉列表中。)

但是,在程序执行之前,你必须连接类模块中被声明的对象到Circle对象,你可以用任何模块中的以下代码来完成这一过程。

连接被声明的对象到Automation对象

1 在主模块的代码窗口中,添加以下行到声明段中:

Dim X As New EventClassModule

2 在相同窗口中,建立一个叫"MyCircle"的圆并把它初始化为包含事件:Sub InitializeEvents()

    Dim MyCircle As AcadCircle

    Dim centerPoint(0 To 2) As Double

    Dim radius As Double

    centerPoint(0) = 0#: centerPoint(1) = 0#: centerPoint(2) = 0#

    radius = 5#

    Set MyCircle = ThisDrawing.ModelSpace.AddCircle(centerPoint, radius)

    Set X.Object = MyCircle

End Sub

3 在你的主模块的代码中,添加对InitializeApp子程序的调用:

Call InitializeEvents

一旦InitializeEvents程序执行后,在类模块中的圆对象就会指向被建立的圆对象。并且当事件发生时,这个分类模块中的任何事件过程都会运行。

注意:当在VBA编写代码时,你必须为每个激活为Modified事件的对象提供一个事件处理器,如果你不能提供一个事件处理器,VBA可能会发生意外终止。

只要一个封闭的多义线被更新就显示它的面积

以下示例如何建立一个带事件的细多义线。只要多义线被改变,多义线的事件处理器就可以显示多义线的新面积。要想触发这个事件,你只需在AutoCAD中改变多义线的尺寸。记住,在事件处理器活动之前,你必须先运行CreatePLineWithEvents子程序。Public WithEvents PLine As AcadLWPolyline



Sub CreatePLineWithEvents()

    ' 本例创建一细多义线

    Dim points(0 To 9) As Double

    points(0) = 1: points(1) = 1

    points(2) = 1: points(3) = 2

    points(4) = 2: points(5) = 2

    points(6) = 3: points(7) = 3

    points(8) = 3: points(9) = 2

    Set PLine = ThisDrawing.ModelSpace. _

                   AddLightWeightPolyline(points)

    PLine.Closed = True

    ThisDrawing.Application.ZoomAll

End Sub



Private Sub PLine_Modified _

               (ByVal pObject As AutoCAD.IAcadObject)

    ' 该事件当多义线大小变化时引发。

    ' 如果多义线被删除时modified 事件仍然会被引发,

    ' 所以使用错误处理器来避免从删除了的对象中读取数据。

    On Error GoTo ERRORHANDLER

    MsgBox "对象" & pObject.ObjectName & " 的面积为: " _

            & pObject.Area

    Exit Sub

   

ERRORHANDLER:

    MsgBox Err.Description

End Sub

[此贴子已经被作者于2005-9-21 23:00:07编辑过]


作者: secowu    时间: 2005-8-22 23:31
好样的搞搞那个条件格式?
作者: LucasLynn    时间: 2005-8-22 23:50
【原创】总结上面文章的核心内容:

(注:下文中引用之所有代码仅作原理说明用,未经调试和测试。实际使用中可能需要作一定的修改)

对于一个包含事件的对象(比方说Access中的Form),我们在通过Set获得其引用的同时,我们也可以捕获他的事件。声明的方法为:Dim/Public/Private WithEvents

例如:

Public WithEvents X As Form

Set A=Forms(0)

Public Sub A_OnLoad()

    '这里的代码将在窗体0被Load的时候执行

End Sub

有人可能会质疑,为什么要这样做,直接写在Form_OnLoad里面不是更方便?这里有个很大的区别,Form_OnLoad中的代码针对的对象是固定的,也就是说你写在窗体1里面就只有窗体1能执行,窗体2则要另外写,而上面的方式则不同,如:(以下代码需要根据VBA的规定分散到窗体和模块中)

Dim Tag As Boolean

Publice WithEvents rs As DAO.RecordSet



Private Sub Command0_Click()

    If Tag then

        Set rs = RecordSet1

    Else

        Set rs = RecordSet2

    End If

End Sub

Publice Sub rs_OnMoveComplete()

    '执行代码

End Sub

我们可以通过按钮来切换我们所要监视的对象,如果和常规方法比较,打个简单的比方,警察要监视100个嫌疑犯,常规的方法是警察想要监视哪个嫌疑犯的时候,要跑一趟他家,把监视器打开,并把刚才监视完毕的那个嫌疑犯家里的监视器关掉,免得占用了通讯频道。而新的做法是警察在自己的监视器上装了一个切换器,能够自由切换接收哪家的监视信号。

如果说上面的例子还没有让你对WithEvents的作用引起足够的重视,那么下面的例子将充分表现WithEvents的有用之处:

Public WithEvents rs As Control

Public Sub SwitchDest(ByVal n As Long)

    Set rs = Forms(0).Controls(n)

End Sub

Private Sub rs_OnChange()

    MsgBox "你所监视的对象被改变了,你可以在这里的代码中对改变后的值作出相应。"

End Sub

看到这里,你应该已经可以明白,新的处理方式将能给我们带来什么,事件并不是在运行时无法变更对象,对事件进行第二层包装后,我们可以随意决定我们想要捕获事件的对象,比方说,根据数据表中的某个字段,来决定今后几个字段的计算方式,以及计算结果要对哪几个值的变更作出相应。

用一个通俗的比喻来理解上述的应用,由于嫌疑犯数量众多,一个警察不堪重负,因此上级决定扩大编制为20个警察,并且根据实际情况的需要还有可能增加,于是这个警察再次重新设计了监视系统,他把监视系统改变成监视信号不直接发到任何一台单独的监视器上,而是集中到一个发射台上,警察的监视器统一接收发射台信号,并且改装了监视器,能够切换不同的频道。



多种的事件处理方法可以使我们设计出更灵活的数据库,当然更多其他的应用需要开发者在使用中共同挖掘。



[此贴子已经被作者于2005-8-22 17:38:51编辑过]


作者: CHENZHIRONG    时间: 2005-8-23 03:40
生动活泼,浅显易懂,对象编程的基本原理。
作者: esmile    时间: 2005-8-23 04:50
提示: 作者被禁止或删除 内容自动屏蔽
作者: sgrshh29    时间: 2005-8-23 04:55
以下是引用CHENZHIRONG在2005-8-22 19:40:00的发言:

生动活泼,浅显易懂,对象编程的基本原理。





[em06][em06][em06]
作者: esmile    时间: 2005-8-23 08:23
提示: 作者被禁止或删除 内容自动屏蔽
作者: LucasLynn    时间: 2005-8-23 22:17
以下是引用esmile在2005-8-23 0:23:00的发言:



哈哈,楼主果然思路开拓,想法故然妙哉,只可惜不能在ACCESS中使用,

所谓此类非彼类也!

个中原因本人一直未能参悟,还望高手指点迷津!

难道ACCESS中不支持WithEvents,为何又不出错?[em06][em06][em06][em06]

附件中有VB与MDB的事件对比,高手看看就明白了.

[attach]12612[/attach]

http://cx66.com/cxgzs/program/vb/465.htm (关于Visual Basic 6.0类开发)





完全可以在Access中使用,只是代码放置的地方VBA有限制,有些代码必须放在模块,有些必须放在类模块。
作者: esmile    时间: 2005-8-23 23:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: LucasLynn    时间: 2005-8-23 23:42
以下是引用esmile在2005-8-23 15:11:00的发言:

不好意思,

不耻下问,不知楼主能否将此程序的位置移移,达到想要的结果.

按楼主的意思,在VB中完全可以行得通,可在ACCESS中,我就犯晕了,

楼主可否抽时间一阅?[attach]12625[/attach]



需要对你接管的事件作一个声明:

Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer)

End Sub

空代码即可。

另外一个方法是在代码中加入:Text1.OnKeyUp="[Event Procedure]"。可以放在Form_Load里面

发生这种情况的原因是Access执行窗体的时候会检查事件处理程序,如果没有找到,会自动清除OnKeyUp的值,而这样KeyUp事件就不会送入VBA运行时了。

[此贴子已经被作者于2005-8-23 16:06:45编辑过]


作者: esmile    时间: 2005-8-24 00:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: LucasLynn    时间: 2005-8-26 23:18
以下是引用esmile在2005-8-23 16:11:00的发言:

真的可以,太爱你了,哈哈



又找到一个方法,在Form_OnLoad里面加入一句:



Text1.OnKeyUp="[Event Procedure]"
作者: esmile    时间: 2005-8-27 06:19
提示: 作者被禁止或删除 内容自动屏蔽
作者: LucasLynn    时间: 2005-8-27 12:27
以下是引用esmile在2005-8-26 22:19:00的发言:



谢谢,真的可以.

我觉得事件处理器很是好用,但就是不知道速度如何.

如不介意,我又发现一个问题.

当我在类事件中定义了引用窗体的_Load 或_OPEN事件如何调用呢?

仁兄不知道能否明白我的意思,哈哈,感兴趣的话研究一下,如何?





可以放到其他窗体里面,那个窗体可以设定为Access启动自动加载,加载后立即关闭,等等等等。

能用的方法太多了。
作者: esmile    时间: 2005-8-27 16:47
提示: 作者被禁止或删除 内容自动屏蔽
作者: LucasLynn    时间: 2005-8-27 20:02
以下是引用esmile在2005-8-27 8:47:00的发言:



谢谢.

自动启动加载?

不知是否这样操作?小弟真是够笨,还是不能实际,LucasLynn 兄能否抽空看看?我还是未明领悟. 谢了.

[em06][attach]12707[/attach]



我只是想,有必要这么做吗?

这样处理事件的方法,主要是用于动态对象,也就是对象实例不可确定的时候,但是窗体本身只有一个对象,而且是确定的,有必要重新截获他的OnLoad吗?有什么事情不能在Form_Load()里面干呢?
作者: esmile    时间: 2005-8-29 16:40
提示: 作者被禁止或删除 内容自动屏蔽
作者: andymark    时间: 2006-7-29 07:56
学习
作者: 古老的龙    时间: 2006-7-30 00:46
楼主你太棒了!!!!!
作者: lkkl66    时间: 2008-8-1 21:27
只是好奇,收藏---说不定有朝一日会真正感兴趣!
作者: wangchaoyong    时间: 2008-10-17 10:39
正在努力学习中,正好需要
[:11] [:13]




欢迎光临 Office中国论坛/Access中国论坛 (http://www.office-cn.net/) Powered by Discuz! X3.3