Autodesk官方最新的.NET教程(七)(vb.net版)

教程

第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参数(或者是一个派生的类),这个对象通常包含用于事件处理函数的数据。 

第1b部分 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) 
        If TypeOf ent Is BlockReference Then 'We use .NET's RTTI to establish type. 
            Dim br As BlockReference = CType(ent, BlockReference) 
            'Test whether it is an employee block 
            'open its extension dictionary 
            If br.ExtensionDictionary().IsValid Then 
                Dim brExtDict As DBDictionary = trans.GetObject(br.ExtensionDictionary(), OpenMode.ForRead) 
                If brExtDict.GetAt("EmployeeData").IsValid Then 
                    'successfully got "EmployeeData" so br is employee block ref 

                    'Store the objectID and the position 
                    changedObjects.Add(objId) 
                    employeePositions.Add(br.Position) 
                    'Get the attribute references,if any 
                    Dim atts As AttributeCollection 
                    atts = br.AttributeCollection 
                    If atts.Count > 0 Then 
                        Dim attId As ObjectId 
                        For Each attId In atts 
                            Dim att As AttributeReference 
                            att = trans.GetObject(attId, OpenMode.ForRead, False) 
                            changedObjects.Add(attId) 
                            employeePositions.Add(att.Position) 

                        Next 
                    End If 
                End If 
            End If 
        End If 
        trans.Commit() 
    Finally 
        trans.Dispose() 
    End Try 
End Sub 
第4步 创建第二个文档事件处理函数(回调函数) 
当一个命令结束时,第三个事件处理函数被调用。同样地,我们要检查全局变量来验证这个将要结束的命令是我们监视的命令。如果是我们监视的,那么我们要重置这个变量: 
If bEditCommand = False Then 
    Return 
End If 

bEditCommand = False 

这个回调函数执行的动作将会再次触发ObjectOpenedForModify事件。我们必须确定在这个回调函数中跳过了所有与此事件有关的动作。 
'设置标志来跳过OpenedForModify处理函数 
bDoRepositioning = True 

这个回调函数的剩余代码用来把EMPLOYEE块索引和它的关联属性引用的当前(修改过的)位置与它们的初始位置作比较。如果位置改变了,我们在这个回调函数中把它们重置这初始的位置。下面的代码可以被粘贴到这个事件处理函数中。 

Public Sub cmdEnded(ByVal o As Object, ByVal e As CommandEventArgs) 
    'Was our monitored command active? 
    If bEditCommand = False Then 
        Return 
    End If 

    bEditCommand = False 

    'Set flag to bypass ObjectOpenedForModify handler 
    bDoRepositioning = True 

    Dim db As Database = HostApplicationServices.WorkingDatabase 
    Dim trans As Transaction 
    Dim bt As BlockTable 
    Dim oldpos As Point3d 
    Dim newpos As Point3d 
    Dim i As Integer 
    Dim j As Integer = 1 
    For i = 0 To changedObjects.Count - 1 
        trans = db.TransactionManager.StartTransaction() 
        Try 
            bt = trans.GetObject(db.BlockTableId, OpenMode.ForRead) 
            Dim ent As Entity = CType(trans.GetObject(changedObjects.Item(i), OpenMode.ForWrite), Entity) 
            If TypeOf ent Is BlockReference Then 'We use .NET's RTTI to establish type. 
                Dim br As BlockReference = CType(ent, BlockReference) 
                newpos = br.Position 
                oldpos = employeePositions.Item(i) 

                'Reset blockref position 
                If Not oldpos.Equals(newpos) Then 
                    trans.GetObject(br.ObjectId, OpenMode.ForWrite) 
                    br.Position = oldpos 
                End If 
            ElseIf TypeOf ent Is AttributeReference Then 
                Dim att As AttributeReference = CType(ent, AttributeReference) 
                 newpos = att.Position 
                oldpos = employeePositions.Item(i) 

                'Reset attref position 
                If Not oldpos.Equals(newpos) Then 
                    trans.GetObject(att.ObjectId, OpenMode.ForWrite) 
                    att.Position = oldpos 
                End If 
            End If 
            bt.Dispose() 
            trans.Commit() 
        Finally 
            trans.Dispose() 
        End Try 
    Next 
End Sub 

第5步  创建命令来注册/断开事件处理函数 
创建一个ADDEVENTS命令,使用+=语句来把上面的3个事件处理函数连接到各自的事件。在这个命令中,我们还应该设置全局Boolean变量: 
bEditCommand = False 
bDoRepositioning = False 
 
创建另外一个命令REMOVEEVENTS,使用RemoveHandler语句把事件处理函数与事件断开。 
第6步: 测试工程 
要测试这个工程,请使用CREATE命令创建一个或多个EMPLOYEE块索引。如果你要作比较的话,你也可以插入一些非EMPLOYEE的块索引。 
在命令行中键入ADDEVENTS命令来执行它。 
在命令行中输入MOVE命令,然后选择你想要的块索引。注意,当MOVE命令结束时,EMPLOYEE块索引(包括属性)还留在原处。 
执行REMOVEEVENTS命令,然后在试一下MOVE命令。注意,EMPLOYEE块索引现在可以被移动了。 

附加的问题:添加一个附加的回调函数,当用户改变EMPLOYEE块索引的”Name”属性时,这个回调函数被触发。 

时间: 2024-08-03 05:16:38

Autodesk官方最新的.NET教程(七)(vb.net版)的相关文章

Autodesk官方最新的.NET教程(一)(VB.NET版)

教程   第 1章         Hello World: 工程的创建        在这一章中,我们将不使用ObjectARX向导来创建一个新的工程.我们将使用Visual Studio .NET来创建一个新的类库工程.通过这个工程,你可以创建一个能被AutoCAD装载的.NET  dll文件.这个dll文件会向AutoCAD加入一个名为"HelloWorld"的新命令.当用户运行这个命令后,在AutoCAD 命令行上将显示"Hello World"文本.1) 

Autodesk官方最新的.NET教程(六)(vb.net版)

教程 第6章 更多的用户界面:添加自定义数据在本章中,我们将介绍.NET API的用户界面部分能做些什么.我们首先将介绍一个自定义上下文菜单(快捷菜单).接下来我们将实现一个无模式可停靠的面板(一个真正的AutoCAD增强辅助窗口)来支持拖放操作.接着我们将介绍通过模式窗体选取实体.最后,我们将介绍使用AutoCAD的选项对话框来设置雇员的缺省值.本章还会介绍和上面内容有关的API. 第一部分 自定义上下文菜单 到目前为止,我们所写的代码只与CommandMethod属性定义的命令行进行相互操作

Autodesk官方最新的.NET教程(四)(vb.net版)

教程 第 4 章 数据库基础2:  添加自定义数据 在这一章中,我们将创建一个新的字典对象,它用来表示我们雇员就职的 'Acme 公司'(呵呵,当然是虚构的一家公司)的部门.这个"部门"字典对象将包含一个表示部门经理的记录.我们还会加入代码到雇员创建过程,这个过程会加入一个索引到雇员工作的部门.我们要说明的是如何在DWG文件中创建自定义数据,包括"每个图形"的自定义数据和"每个实体"的自定义数据."每个图形"的自定义数据是指只

Autodesk官方最新的.NET教程(二)(VB.NET版)

教程   第2章  .NET AutoCAD 向导及简单用户输入        在第一章中,我们使用的是类库模板,这样就不得不手工加入acdbmdg. dll 和acmgd.dll这两个引用.在这一章中,我们将使用AutoCAD托管C#应用程序向导来创建.NET工程,它会自动加入以上两个引用.在开始本章之前,你首先得安装ObjectARX向导(ObjectARX2006开发包的\utils\ObjARXWiz\ArxWizards.msi).   1)    启动Visual Studio .N

Autodesk官方最新的.NET教程(五)(vb.net版)

教程   第 5 章 用户互操作:提示和选择背景提示通常包含一个描述性信息,伴随一个停止以让用户理解所给的信息并输入数据.数据可以通过多种方式被输入,如通过命令行.对话框或AutoCAD编辑窗口.给出的提示要遵循一定的格式,格式要与一般的AutoCAD提示相一致,这一点是非常重要的.例如,关键字要用"/"号分隔并放在方括号"[]"中,缺省值要放在"<>"内.对于一个AutoCAD用户来说,坚持统一的格式将会减少信息理解错误的产生.当用

Autodesk官方最新的.NET教程(三)(vb.net版)

教程 第 3 章 数据库基础:  创建我们自己的Employee 对象 打开Lab3文件夹下的Lab3工程文件,或或接着Lab2的代码.在这一章中,我们将创建一个'Employee 对象'(包括一个圆,一个椭圆和一个多行文本对象),这个对象属于一个自定义的EmployeeBlock'块(这个块驻留在'EmployeeLayer'层,当在模型空间插入这个块的时候,'EmployeeLayer'层就会拥有这个块的一个块索引).本章的每一个步骤中的代码都可以运行,这样做的目的可以使你更清楚地知道每一部

Autodesk官方最新的.NET教程(七)(C#版)

教程 第7章 事件 本章将讨论AutoCAD中的事件.我们将介绍事件处理函数的使用,特别是监视AutoCAD命令的事件处理函数和监视被AutoCAD命令修改的对象的事件处理函数.在解释怎样在C#中实现AutoCAD的事件处理之前,我们将首先简要地讨论一下.NET中的事件.  第一部分 C#中的事件 事件只是用来通知一个行为已经发生的信息.在ObjectARX中,我们使用反应器(reactor)来处理AutoCAD的事件.而在AutoCAD .NET API中,ObjectARX反应器被换成了事件

Autodesk官方最新的.NET教程(一)(C#版)

教程   第 1章         Hello World: 访问 ObjectARX .NET 封装类       在这一章中,我们将使用Visual Studio .NET来创建一个新的类库工程.通过这个工程,你可以创建一个能被AutoCAD装载的.NET  dll文件.这个dll文件会向AutoCAD加入一个名为"HelloWorld"的新命令.当用户运行这个命令后,在AutoCAD 命令行上将显示"Hello World"文本. 1)         启动V

Autodesk官方最新的.NET教程(六)(C#版)

教程 第6章 更多的用户界面:添加自定义数据 在本章中,我们将介绍.NET API的用户界面部分能做些什么.我们首先将介绍一个自定义上下文菜单(快捷菜单).接下来我们将实现一个无模式可停靠的面板(一个真正的AutoCAD增强辅助窗口)来支持拖放操作.接着我们将介绍通过模式窗体选取实体.最后,我们将介绍使用AutoCAD的选项对话框来设置雇员的缺省值.本章还会介绍和上面内容有关的API. 第一部分 自定义上下文菜单 到目前为止,我们所写的代码只与CommandMethod属性定义的命令行进行相互操