注册 登录
明经CAD社区 返回首页

sieben的个人空间 http://www.mjtd.com/?43121 [收藏] [复制] [分享] [RSS]

日志

C# 处理AutoCAD事件

已有 3861 次阅读2007-7-31 15:36 |系统分类:开发

今日没事!翻译AutoCAD .NET API 培训文件玩玩!

玩玩而已!有错莫怪!即使翻译得很恶心也请顶一下!

内容是AutoCAD .NET API 培训文件的第七章

另外:在AutoCAD .NET API 的文件包里有对应的全部工程代码

Lab 7 – Handling Events in AutoCAD
处理AutoCAD事件
In this lab, we explore Events in AutoCAD.  We will discuss the use of event handlers; specifically, to monitor AutoCAD commands as well as monitor objects which are about to be modified by those commands.  We begin with a brief discussion of events in .NET, before proceeding to demonstrate how to implement AutoCAD event handlers in C#.
在这一章,我们分析AutoCAD里的事件,我们将讨论如何使用事件处理,特别是监视AutoCAD命令如监视对象如何被AutoCAD命令修改,在开始说明如何引入AutoCAD事件处理之前, 我们先概略的讨论.Net事件.
Events in C#
An event is simply a message sent to notify that an action has taken place.  In ObjectARX, we refer to reactors to model AutoCAD events.  In the AutoCAD .NET API, the ObjectARX reactors are mapped to events.
一个事件是一个简单的信息提示说有个动作发生了,在ObjectARX,我们参考反应器构建AutoCAD事件,在AutoCAD .NET程序接口, ObjectARX封装到事件里
Event handlers (or callbacks) are procedures which are placed in environment to watch and react to events that occur in the application. Events come in a variety of types.
程序里设置了事件处理器(或回调函数),事件处理器会监视事件的发生并被调用对事件作出响应
As an introduction to working with events in AutoCAD's .NET API, a brief description of delegates may be helpful.
作为应用AutoCAD's .NET API事件的入门,简单的介绍委托或许会有帮助
Delegates Described
委托描述
A delegate is a class that holds a reference to a method (the functionality is similar to function pointers). Delegates are type-safe references to methods (similar to function pointers in C).  They have a specific signature and return type.  A delegate can encapsulate any method which matches the specific signature.
一个委托是拥有一个方法的引用的类(其作用类似于一个函数指针),委托是类安全的方法引用(类似C里的函数指针),他们拥有专有签名和返回类型,一个委托可以封装监视这种专有签名的方法
Delegates have several uses, one of which is acting as a dispatcher for a class that raises an event.  Events are first-class objects in the .NET environment.  Even though C# hides much of the implementation detail, events are implemented with delegates.  Event delegates are multicast (meaning they hold references to more than one event handling method).  They maintain a list of registered event handlers for the event.  A typical event-handling delegate has a signature like the following:
委托有几个用途,其中之一是作为一个类的事件发生器,在.NET环境里事件是底层类,事件委托是多关联的(即一个事件委托可以关联多个事件处理器函数),他们构成事件的处理器表,一个典型的事件处理委托有0下面的签名形式:
 public delegate Event (Object sender, EventArgs e)
The first argument, sender, represents the object that raises the event.
第一个参数sender,代表引发事件的对象
The second, e, is an EventArgs object (or a class derived from such).  This object generally contains data that would be of use to the events handler.
第二个参数e,是一个EventArgs对象(或EventArgs派生对象),这个参数通常包含用于事件处理的数据
C# += and -= statements
In order to use an event handler, we must associate it with an event.  This is done by using either the += statement.  += and its counterpart -=, allow you to connect, disconnect, or change handlers associated with the event at run time.
要使用一个事件处理器,我们首先要将其与一个事件关联,这个由+=语句实现,在运行时,+=及其配对的-=连接,断开或改变处理器与事件的关联
When we use the += statement, we specify the name of the event sender, and we specify the name of our event handler with the new statement; for example:
当我们使用+=语句,我们指定引发事件的名字,并使用new语句来指定事件处理器的名字,例如:
MyClass1.AnEvent += new  HandlerDelegate(EHandler)

As mentioned, we use the -= statement to disconnect an event from an event handler (remove the association).  The syntax is as follows:
顺便说一下,我们使用-+语句来断开事件和其处理器(移除之间的关联),语法如下:
MyClass1.AnEvent -= new  HandlerDelegate(EHandler)
Handling AutoCAD Events in .NET
AutoCAD  .NET的事件处理
In ObjectARX, we refer to reactors to model AutoCAD events.  In the AutoCAD .NET API, the ObjectARX reactors are mapped to events.   
在ObjectARX,我们参考反应器去构建AutoCAD事件,在AutoCAD .NET API, ObjectARX反应器都封装成事件
In general, the steps for dealing with AutoCAD events are:
一般地, AutoCAD事件使用如下:
1. Create the event handler.
1,创建一个事件处理器
An event handler (or callback) is the procedure to be called when an event is raised (triggered).  Any action we wish to take, in response to an AutoCAD event, takes place in the event handler.
当一个事件发生(触发)时一个事件处理器(或回调函数)将会被调用执行,响应AutoCAD事件的任何动作在事件处理器里发生
For example, suppose we just want to notify the user that an AutoCAD object has been appended.  We can use the AutoCAD database event “ObjectAppended” to accomplish this.  We can write our callback (event handler) as follows:
例如:假设我们只是通报用户一个AutoCAD对象已经被添加,我们可以用AutoCAD数据库事件“ObjectAppended”达成,我们可以写回调函数(事件处理器)如下:
public void objAppended(object o, ObjectEventArgs e)
{
 // Do something here
}
The first argument, in this case, represents an AutoCAD database.  The second represents the ObjectEventArgs class, which may contain data that is useful to the handler.
在这里,第一个参数是AutoCAD数据库,第二个参数是ObjectEventArgs,可能含有处理器有用的数据
2. Associate the event handler with an event.
2.建立事件处理器与事件的关联
In order to begin monitoring an action, we must connect our handler to the event.
为了开始监控一个行为,我们必须将事件处理器关联到事件上
At this point, the ObjectAppended event will fire when an object is added to the database.  However, our handler will not respond to it until we associate it to the event, such as:
在这里, 当一个对象加到数据库,ObjectAppended被触发,然而,我们的处理器将不作反应除非我们将它关联到事件,如下:
Database db; 
db = HostApplicationServices.WorkingDatabase;
db. ObjectAppended += new ObjectEventHandler(objAppended);
3. Disconnect the event handler.
3.断开事件处理器
To cease monitoring an action, we must remove the association between our handler and the event.  When we want to stop notifying the user when objects are appended, we need to remove the association between our handler and the ObjectAppended event:
要终止一个监视,我们必须移除事件与事件处理器的关联,若我们想停止向用户通报对象添加,我们需要移除我们的事件处理器和ObjectAppended事件的关联
db. ObjectAppended -= new ObjectEventHandler(objAppended);

Lab: Using event handlers’ to control AutoCAD behavior
测验:使用事件处理器控制AutoCAD行为
The objective of Lab 7 is to demonstrate how AutoCAD events can be used to control behavior in a drawing.  In this case, let us assume that we have used the previous lab (Lab 6), to create some EMPLOYEE block references in a drawing.  We want to prevent the user from changing the position of the EMPLOYEE block reference in the drawing, without limiting the location of other (non-EMPLOYEE) block references.  We will do this through a combination of Database and Document events.
第七章的目的是说明如何使用AutoCAD事件去控制图纸里的行为,在这里,我们假设已经用前面一章(第六章)的方法在图纸里生成了一些EMPLOYEE块参考,我们想防止用户改变这些块参考在图纸里的位置,而不限制其他(非EMPLOYEE)块参考,我们将通过绑定数据库和文件事件来实现
We first want to monitor AutoCAD commands as they are about to be executed (we use the CommandWillStart event).  Specifically we are watching for the MOVE command.  We also need to be notified when an object is about to be modified (using the ObjectOpenedForModify event), so we can that it is an EMPLOYEE block reference.  It would be futile to modify the object from this callback, as our change would just re-trigger the event, causing unstable behavior.  So, we will wait for the execution of the MOVE command to end (using the CommandEnded event).  This would be a safe time to modify our object.  Of course any modification to the block reference will again trigger the ObjectOpenedForModify event.  However, we will set some global variables as flags, to indicate that a MOVE command is active, and that the object being modified is an EMPLOYEE block reference.
首先我们想监控将被执行的AutoCAD命令(我们使用CommandWillStart事件),特别地我们监视Move命令,同时当一个对象将要被修改时我们需要通报(使用ObjectOpenedForModify事件) 因此我们能知道它是一个EMPLOYEE块参考,从回调函数里修改对象将是无效的,因为修改将重新触发事件,会导致不可预测行为,因此我们要等到Move命令结束(使用CommandEnded事件),这时会是修改我们对象的安全时间,当然,对块参考的任何修改同样又会触发ObjectOpenedForModify事件,但是,我们可以设置一些全局变量作为标记,去表明Move命令执行中和正在修改的对象是EMPLOYEE块参考
NOTE: Since this lab requires a considerable amount of code to produce the desired result, any code not specifically dealing with reactors is provided, so as to be pasted into the event handlers.  The emphasis at this time is on the successful creation of the event handlers and their registration.
备注:因为这一章需要大量的代码,任何不是和反应器有关的代码都已提高,只要贴到事件处理器里,这里重点是如何成功创建事件处理器及注册
Setup the new project
建立一个新项目
Begin with the solved Lab6 project.  Add a new class AsdkClass2.  We will need to add four global variables.  The first two are of type Boolean: one to indicate that our monitored command is active, and one to indicate that the ObjectOpenedForModify handler should be bypassed.
从第六章的解决方案开始,增加一个新类AsdkClass2,我们需要加入四个全局变量,前面两个是Boolean类型,一个表述当前命令是否是我们监视的命令,另外一个用来表述ObjectOpenedForModify事件处理器是否忽略
//Global variables 
bool bEditCommand;
bool bDoRepositioning; 
 
Next, we declare a global variable which represents an ObjectIdCollection.  This will hold the ObjectIDs of the objects we have selected to modify. 
接着,我们声明一个全局变量代表一个ObjectIdCollection,用来记录已经选择去修改的对象ObjectId
ObjectIdCollection changedObjects = new ObjectIdCollection();
Finally, we declare a global variable which represents a Point3dCollection.  This collection contains the position (3dPoint) of our selected objects.
最后,我们声明一个Point3dCollection全局变量,这个变量保存选择对象的位置
 Point3dCollection employeePositions = new Point3dCollection();
Create the first Document event handler (callback)
创建第一个文件事件处理器
Now we must create an event handler which notifies us when an AutoCAD command is about to start.  We should check that the GlobalCommandName = MOVE
现在我们必须创建一个事件处理器来通报一个AutoCAD命令将开始,我们需要检查它的全局命令名是MOVE
if ( e.GlobalCommandName == "MOVE" )
{
}
If the MOVE command is about to start, we need to set our Boolean variable bEditCommand accordingly, so we know that our monitored command is active.  Likewise, we should set our other Boolean variable bDoRepositioning to NOT bypass the ObjectOpenedForModify event handler at this time.  After all, it is during this period, while the command is active, that we must acquire information about our selected block references.
如果是MOVE命令将被执行,需要给变量bEditCommand赋值为真,因而我们知道当前命令为我们需要监控的命令,同样的我们应该设置bDoRepositioning为真来告诉ObjectOpenedForModify事件处理器这时不要忽略,毕竟,在这个命令执行期间,我们需要获取这些选择块参考的信息
At this time, we should also clear any contents from our two Collection objects.  We are only concerned with the currently-selected object.
在这时,我们要清空两个集合变量,我们只关心当前选择的对象
Create the Database event handler (callback)
创建数据库事件处理器
This event handler will be called whenever an object has been opened for modification.  Of course, if our monitored command is not active at this time, we should bypass any further processing done by this callback:
这个事件处理器在有对象被打开修改时将被调用,当然,如果我们监控的命令这时并不执行,我们没必要在这个回调函数了做进一步的动作
if ( bEditCommand == false )
{
 return;
}
Similarly, if our monitored command has ended, and the ObjectOpenedForModify event is re-triggered by some action taken in another callback, we want to prevent any subsequent executions of this callback while the object is being modified:  
同样的,如果我们监控的命令已经结束, 同时ObjectOpenedForModify事件被另外的回调函数触发,我们需要防止这个回调函数在对象修改过程中的并发执行
if ( bDoRepositioning == true )
{
 return;
}
The remainder off the code in this callback is used to validate that we are indeed processing an EMPLOYEE block reference.  If so, we collect its ObjectID and its Position (3dPoint).  The following code can be pasted into this event handler:
这个回调函数余下的代码时确认我们正在处理一个EMPLOYEE块参考,如果是,收集它的ObjectID和位置坐标
public void objOpenedForMod(object o, ObjectEventArgs e)
{
 if ( bEditCommand == false )
 {
  return;
 }
 if ( bDoRepositioning == true )
 {
  return;
 }

 ObjectId objId;
 objId = e.DBObject.ObjectId;

 Transaction  trans; 
 Database  db; 
 db = HostApplicationServices.WorkingDatabase;

 trans = db.TransactionManager.StartTransaction();

 using(Entity ent  = (Entity)trans.GetObject(objId, OpenMode.ForRead, false))
 {
  if ( ent.GetType().FullName.Equals( "Autodesk.AutoCAD.DatabaseServices.BlockReference" ) )
  {
             //We use .NET//s RTTI to establish type.
      BlockReference br   = (BlockReference)ent;
      //Test whether it is an employee block
             //open its extension dictionary
     if ( br.ExtensionDictionary.IsValid )
   {
   using(DBDictionary brExtDict  = (DBDictionary)trans.GetObject(br.ExtensionDictionary, OpenMode.ForRead))
   {
    if ( brExtDict.GetAt("EmployeeData").IsValid )
    {
  //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
     AttributeCollection atts; 
     atts = br.AttributeCollection;
     if ( atts.Count > 0 )
     {
      foreach(ObjectId attId in atts )
      {
       AttributeReference att;
       using(att = (AttributeReference)trans.GetObject(attId, OpenMode.ForRead, false))
       {
        changedObjects.Add(attId);                                                          employeePositions.Add(att.Position);
       }
        }
      }
     }
    }
   }
  }
 }
 trans.Commit();
}

Create the second Document event handler (callback)
创建第二个文件事件处理器
The third event handler is called when a command ends.  Again, we check our global variable to verify that it is our monitored command that is ending.  If so, we can reset the variable now:
第三个事件处理器在命令结束时被调用,同样,我们检查我们的全局变量确认是我们监控的命令正在结束,如果是,我们重置变量
if ( bEditCommand == false )
{
 return;
}
bEditCommand = false;
Actions taken by this callback will re-trigger the ObjectOpenedForModify event.  We must ensure that we bypass any action in the callback for that event:
这个回调函数的动作将会再次触发ObjectOpenedForModify事件,我们要确保这是在回调函数里忽略这个事件
//Set flag to bypass OpenedForModify handler
bDoRepositioning = true;
The remainder off the code in this callback is used to compare the current (modified) positions of an EMPLOYEE block reference and its associated attribute reference to their original positions.  If the positions have changed, we reset them to the original positions during his callback.  The following code can be pasted into this event handler:
接下来的代码是对比目前(修改)的EMPLOYEE块参考和它对应的原始位置,如果位置改变了,我们在这个回调函数恢复到原始的位置,下面的代码可以贴到事件处理器里
public void cmdEnded(object o  , CommandEventArgs e)
{
 //Was our monitored command active?
 if ( bEditCommand == false )
 {
  return;
 }
  bEditCommand = false;
 //Set flag to bypass OpenedForModify handler
 bDoRepositioning = true;
  Database db   = HostApplicationServices.WorkingDatabase;
 Transaction trans ;
 BlockTable bt; 
 Point3d oldpos; 
 Point3d newpos; 
 int i ;
 for ( i = 0; i< changedObjects.Count; i++)
 {
  trans = db.TransactionManager.StartTransaction();
  using(bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead))
  {
   using(Entity ent = (Entity)trans.GetObject(changedObjects[i], OpenMode.ForWrite))
   {
    if ( ent.GetType().FullName.Equals("Autodesk.AutoCAD.DatabaseServices.BlockReference") )
    {
                            //We use .NET//s RTTI to establish type.
     BlockReference br = (BlockReference)ent;
     newpos = br.Position;
     oldpos = employeePositions[i];
     //Reset blockref position
     //恢复块参考的位置
     if ( !oldpos.Equals(newpos) )
     {
      using( trans.GetObject(br.ObjectId, OpenMode.ForWrite) )
      {
       br.Position = oldpos;
      }
     }
    }
    else if ( ent.GetType().FullName.Equals("Autodesk.AutoCAD.DatabaseServices.AttributeReference") )
    {
     AttributeReference att = (AttributeReference)ent;
     newpos = att.Position;
     oldpos = employeePositions[i];
     //Reset attref position
     //恢复块参考特性的位置
     if ( !oldpos.Equals(newpos) )
     {
      using( trans.GetObject(att.ObjectId, OpenMode.ForWrite))
      {
       att.Position = oldpos;
      }
     }
    }
   }
  }
  trans.Commit();
 }
}

Create the commands to register/disconnect the event handlers
创建命令来注册和断开事件处理器
Create a command ADDEVENTS, which uses += statements to associate each of the three event handlers to the events.  During this command, we should also set our global Boolean variables:
创建ADDEVENTS命令,使用+=语句来建立三个事件处理器与各事件的关联,在这个命令中,我们同时设置Boolean类变量
bEditCommand = false;
bDoRepositioning = false;
 
Create another command REMOVEEVENTS, using -= statements to disconnect our event handlers from the events. 
创建REMOVEEVENTS命令,使用+=语句来从事件断开事件处理器
Test the project
测试工程
To test this project, Create one or more EMPLOYEE block references, using the CREATE command.  For comparison, also insert some non-EMPLOYEE block references, if you like.
为了测试这个工程,使用CREATE命令生成一个或多个EMPLOYEE块参考,如果喜欢,可以插入一些非EMPLOYEE块参考以作比较
Execute the ADDEVENTS command by typing it into the command window. 
在命令行窗口输入执行ADDEVENTS命令
Execute the MOVE command at the command window, and select as many block references as you want.  Note that when the MOVE command ends, the EMPLOYEE block references (and attributes) retain their original positions. 
从命令行执行MOVE命令,你想多少就选择多少块参考,注意,当MOVE结束时, EMPLOYEE块参考(和特性)停留在原来的位置
Execute the REMOVEEVENTS command, and try the MOVE command again.  Note that the EMPLOYEE block references can now be moved.
执行REMOVEEVENTS命令,并再次执行MOVE命令,注意现在EMPLOYEE可以被移动了
Extra credit:  Add an additional callback which is triggered when the EMPLOYEE block reference “Name” attribute has been changed by the user.
额外任务:增加一个回调函数,当用户修改EMPLOYEE块参考的”Name”特性时触发


路过

雷人

握手

鲜花

鸡蛋

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-4-26 06:04 , Processed in 0.109914 second(s), 15 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

返回顶部