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 |