** 第7章 事件 ** **
**
本章将讨论 AutoCAD 中的事件。我们将介绍事件处理函数的使用,特别是监视 AutoCAD 命令的事件处理函数和监视被 AutoCAD 命令修改的对象的事件处理函数。在解释怎样实现 AutoCAD 的事件处理之前,我们将首先简要地讨论一下 .NET 中的事件。
** 第一部分 ** ** VB.NET ** ** 中的事件 ** **
**
事件只是用来通知一个行为已经发生的信息。在 ObjectARX 中,我们使用反应器 (reactor) 来处理 AutoCAD 的事件。而在 AutoCAD .NET API 中, ObjectARX 反应器被换成了事件。
事件处理函数(或者叫回调函数)是用来监视和反馈程序中出现的事件。事件可以以不同的形式出现。
在介绍 AutoCAD .NET API 中的事件之前,让我们先来简单地了解一下代理。
** 第 ** ** 1a ** ** 部分 代理 ** **
**
代理是一个存储方法索引的类(概念与函数指针类似)。代理对方法是类型安全的(与 C 中的函数指针类似)。代理有特定的形式和返回类型。代理可以封装符合这种特定形式的任何方法。
代理的一个用途就是作为产生事件的类的分发器。事件是 .NET 环境中第一级别的对象。虽然 VB.NET 把事件处理的许多细节给隐藏掉了,但事件总是由代理来实现的。事件代理可以多次调用(就是它们可以存储多于1个的事件处理方法的索引)。它们保存了用于事件的一个注册事件处理的列表。一个典型的代理有以下的形式:
Public Delegate Event (sender as Object , e as EventArgs)
第一个参数 sender 表示引发事件的对象。第二个参数 e 是一个 EventArgs 参数(或者是一个派生的类),这个对象通常包含用于事件处理函数的数据。
** 第1 ** ** b ** ** 部分 ** ** AddHandler ** ** 和 ** ** RemoveHandler ** ** 语句 ** **
**
要使用事件处理函数,我们必须把它与事件联系起来。这要通过使用 ** AddHandler ** 语句。 ** AddHandler ** 和 ** RemoveHandler ** 允许你在运行时连接、断开或修改与事件联系的处理函数。
当我们使用 ** AddHandler ** 语句时,我们要确定事件引发者的名字,并要使用 AddressOf 语句来确定事件处理函数,例如:
AddHandler MyClass1.AnEvent, AddressOf EHandler
前面我们说过要使用 RemoveHandler 语句从事件处理函数中断开事件(移除联系)。语法如下所示:
RemoveHandler MyClass1.AnEvent, AddressOf EHandler
** 第2部分 处理 ** ** .NET ** ** 中的 ** ** AutoCAD ** ** 事件 ** **
**
在 ObjectARX 中,我们使用反应器来封装 AutoCAD 事件。在 AutoCAD .NET API 中,我们可以使用事件来代替 ObjectARX 反应器。
通常,处理 AutoCAD 事件的步骤如下:
1. 创建事件处理函数
当一个事件发生时,事件处理函数(或称为回调函数)被调用。任何我们想要处理的回应 AutoCAD 事件的动作都在事件处理函数中进行。
例如,假定我们只想通知用户一个 AutoCAD 对象已被加入。我们可以使用 AutoCAD 数据库事件 ”ObjectAppended” 来完成。我们可以编写回调函数(事件处理函数)如下:
Sub objAppended( ByVal o As Object , ByVal e As ObjectEventArgs)
MessageBox.Show("ObjectAppended!")
‘ 在这里加入一些代码
End Sub
函数中的第一个参数代表 AutoCAD 数据库。第二个参数代表 ObjectEventArgs 类,它可能包含对处理函数有用的数据。
2. 把事件处理函数与事件联系起来
为了开始监视动作,我们必须把事件处理函数与事件联系起来。在这里,当一个对象加入到数据库时, ObjectAppended 事件将会发生。但是,事件处理函数不会响应这个事件,除非我们把它与这个事件联系起来,例如:
Dim db As Database
db = HostApplicationServices.WorkingDatabase()
AddHandler db.ObjectAppended, New ObjectEventHandler( AddressOf objAppended)
3. 断开事件处理函数
要终止监视一个动作,我们必须断开事件处理函数与事件的联系。当对象被加入时,我们想要停止通知用户这个事件,我们要断开事件处理函数与事件 ObjectAppended 的联系。
RemoveHandler db.ObjectAppended, AddressOf objAppended
** 第3部分 使用事件处理函数来控制 ** ** AutoCAD ** ** 的行为 ** **
**
本章的目的是解释 AutoCAD 事件怎样才能被用于控制 AutoCAD 图形中的行为。现在,让我们使用前一章(第六章)的内容在 AutoCAD 图形中创建几个 EMPLOYEE 块索引。我们不想让用户能改变 EMPLOYEE 块索引的位置,而对于其它的非 EMPLOYEE 块索引的位置则没有这个限制。我们将混合使用数据库与文档事件来做到这一点。
首先,我们想要监视将要被执行的 AutoCAD 命令(使用 CommandWillStart 事件)。特别地,我们要监视 MOVE 命令。另外,当一个对象要被修改时,我们应该被通知(使用 ObjectOpenedForModify 事件),这样我们可以确定它是否为一个 EMPLOYEE 块索引。如果这时就修改对象可能是无效的,因为我们的修改可能会再次触发事件,从而引起不稳定的行为。所以,我们要等待 Move 命令的执行结束(使用 CommandEnded 事件),这时就可以安全地修改对象了。当然,任何对块索引的修改将会触发 ObjectOpenedForModify 事件。我们还需要设置一些全局变量来表明一个 MOVE 命令在运行和被修改的对象是一个 EMPLOYEE 块索引。
注意:因为本章需要比较多的代码来获得想要的结果,所以我们不会解释任何与事件处理无关的代码,而只是将它们粘贴到事件处理函数中。这里的重点是成功创建和注册事件处理函数。
** 第一步:创建新工程 ** **
**
我们以第六章的工程开始。请新加入一个类 AsdkClass2 。我们还要加入四个全局变量。前两个是 Boolean 型的:一个用来表示我们监视的命令是否是活动的,另外一个用来表示 ObjectOpenedForModify 事件处理函数是否该被忽略。
' 全局变量
Dim bEditCommand As Boolean
Dim bDoRepositioning As Boolean
接下来,我们要声明一个全局变量来表示一个 ObjectIdCollection ,它用来存储我们所选择的要修改的对象的 ObjectID 。
Dim changedObjects As New ObjectIdCollection()
最后,我们要声明一个全局变量来表示一个 Point3dCollection ,它用来包含我们所选对象的位置(三维点)。
Dim employeePositions As New Point3dCollection()
** 第2步:创建第一个文档事件处理函数(回调函数) ** **
**
现在我们要创建一个事件处理函数。当 AutoCAD 命令开始执行的时候它会通知我们。我们要检查 GlobalCommandName 的值是否为 MOVE 。
If e.GlobalCommandName = "MOVE" Then
'Set the global variables
‘
‘
‘'Delete all stored information
‘
‘
End If
如果 MOVE 命令开始执行的话,我们要相应地设置 Boolean 变量 bEditCommand 的值,这样我们可以知道我们所监视的命令是活动的。同样地,我们应该把另外一个 Boolean 变量 bDoRepositioning 设置为 false 来忽略 ObjectOpenedForModify 事件处理函数。两个变量设置好以后,在命令活动期间,我们必须要获得所选块索引的信息。
我们还应该把两个集合对象的内容清空。我们只关心当前选择的对象。
** 第3步: 创建数据库事件处理函数(回调函数) ** **
**
无论什么时候一个对象被打开并要被修改时,数据库事件处理函数会被调用。当然,如果这时我们监视的命令不是活动的,我们就应该跳过任何被这个回调函数调用的内容。
If bEditCommand = False Then
Return
End If
同样地,如果我们监视的命令已经结束,而 ObjectOpenedForModify 事件被另一个回调函数再次触发的话,而这时有对象被修改时,我们要阻止所有由这个回调函数执行的动作。
If bDoRepositioning = True Then
Return
End If
这个回调函数剩余部分的代码用来验证我们是否正在处理 EMPLOYEE 块索引。如果是的话,我们就获取它的 ObjectID 和位置(三维点)。下面的代码可以被粘贴到这个事件处理函数函数。
Public Sub objOpenedForMod( ByVal o As Object , ByVal e As ObjectEventArgs)
If bEditCommand = False Then
Return
End If
If bDoRepositioning = True Then
Return
End If
Dim objId As ObjectId
objId = e.DBObject.ObjectId
Dim trans As Transaction
Dim bt As BlockTable
Dim db As Database
db = HostApplicationServices.WorkingDatabase
trans = db.TransactionManager.StartTransaction()
Try
'Use it to open the current object!
Dim ent As Entity = trans.GetObject(objId, OpenMode.ForRead, False )
<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; mso-