.NET下防止AutoCAD块被炸开

<Preventing an AutoCAD block from being exploded using .NET>


In response to these recent posts, I received a comment from Nick:

By any chance would it be possible to provide an example to prevent a user from using the EXPLODE command for a given block name?

I delved into the ADN knowledgebase and came across this helpful ObjectARX DevNote, which I used to create a .NET module to address the above question.

Here's the C# code, which should contain enough comments to make it self-explanatory:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;


namespace ExplosionPrevention

{

public class Commands

{

private Document _doc;

private Database _db;


private ObjectIdCollection _blkDefs =

   new ObjectIdCollection();

private ObjectIdCollection _blkRefs =

   new ObjectIdCollection();

private ObjectIdCollection _blkConts =

   new ObjectIdCollection();


private bool _handlers = false;

private bool _exploding = false;


[CommandMethod("STOPEX")]

public void StopBlockFromExploding()

{

   _doc =

      Application.DocumentManager.MdiActiveDocument;

   _db = _doc.Database;


   if (!_handlers)

   {

      AddEventHandlers();

      _handlers = true;

   }


   // Get the name of the block to protect


   PromptStringOptions pso =

      new PromptStringOptions(

      "\nEnter block name: "

      );

   pso.AllowSpaces = false;

   PromptResult pr =

      _doc.Editor.GetString(pso);


   if (pr.Status != PromptStatus.OK)

      return;


   Transaction tr =

      _db.TransactionManager.StartTransaction();

   using (tr)

   {

      // Make sure the block definition exists


      BlockTable bt =

      (BlockTable)

         tr.GetObject(

            _db.BlockTableId,

            OpenMode.ForRead

         );

      if (bt.Has(pr.StringResult))

      {

      // Collect information about the block...


      // 1. the block definition


      ObjectId blkId =

         bt[pr.StringResult];

      _blkDefs.Add(blkId);


      BlockTableRecord btr =

         (BlockTableRecord)

            tr.GetObject(

            blkId,

            OpenMode.ForRead

            );


      // 2. the block's contents


      foreach (ObjectId id in btr)

         _blkConts.Add(id);


      // 3. the block's references


      ObjectIdCollection blkRefs =

         btr.GetBlockReferenceIds(true, true);

      foreach (ObjectId id in blkRefs)

         _blkRefs.Add(id);

      }

      tr.Commit();

   }

}


private void AddEventHandlers()

{

   // When a block reference is added, we need to

   // check whether it's for a block we care about

   // and add it to the list, if so


   _db.ObjectAppended +=

      delegate(object sender, ObjectEventArgs e)

      {

      BlockReference br =

         e.DBObject as BlockReference;

      if (br != null)

      {

         if (_blkDefs.Contains(br.BlockTableRecord))

            _blkRefs.Add(br.ObjectId);

      }

      };


   // Conversely we need to remove block references

   // that as they're erased


   _db.ObjectErased +=

      delegate(object sender, ObjectErasedEventArgs e)

      {

      // This is called during as part of the cloning

      // process, so let's check that's not happening


      if (!_exploding)

      {

         BlockReference br =

            e.DBObject as BlockReference;

         if (br != null)

         {

            // If we're erasing, remove this block

            // reference from the list, otherwise if

            // we're unerasing we will want to add it

            // back in


            if (e.Erased)

            {

            if (_blkRefs.Contains(br.ObjectId))

               _blkRefs.Remove(br.ObjectId);

            }

            else

            {

            if (_blkDefs.Contains(br.BlockTableRecord))

               _blkRefs.Add(br.ObjectId);

            }

         }

      }

      };


   // This is where we fool AutoCAD into thinking the

   // block contents have already been cloned


   _db.BeginDeepClone +=

      delegate(object sender, IdMappingEventArgs e)

      {

      // Only for the explode context


      if (e.IdMapping.DeepCloneContext !=

            DeepCloneType.Explode)

         return;


      // We add IDs to the map to stop the

      // block contents from being cloned


      foreach (ObjectId id in _blkConts)

         e.IdMapping.Add(

            new IdPair(id, id, true, true, true)

         );

      };


   // And this is where we remove the mapping entries


   _db.BeginDeepCloneTranslation +=

      delegate(object sender, IdMappingEventArgs e)

      {

      // Only for the explode context


      if (e.IdMapping.DeepCloneContext !=

            DeepCloneType.Explode)

         return;


      // Set the flag for our CommandEnded handler


      _exploding = true;


      // Remove the entries we added on BeginDeepClone


      foreach (ObjectId id in _blkConts)

         e.IdMapping.Delete(id);

      };


   // As the command ends we unerase the block references


   _doc.CommandEnded +=

      delegate(object sender, CommandEventArgs e)

      {

      if (e.GlobalCommandName == "EXPLODE" && _exploding)

      {

         // By this point the block contents should not have

         // been cloned, but the blocks have been erased


         Transaction tr =

            _db.TransactionManager.StartTransaction();

         using (tr)

         {

            // So we need to unerase each of the erased

            // block references


            foreach (ObjectId id in _blkRefs)

            {

            DBObject obj =

               tr.GetObject(

                  id,

                  OpenMode.ForRead,

                  true

               );


            // Only unerase it if it's needed


            if (obj.IsErased)

            {

               obj.UpgradeOpen();

               obj.Erase(false);

            }

            }

            tr.Commit();

         }

         _exploding = false;

      }

      };

}

}

}

The STOPEX command takes a block name and then gathers (and stores) information about a block: its ObjectId, the IDs of its contents and its various block references. I've added some logic to handle creation of new block references (e.g. via INSERT), and erasure of ones that are no longer needed. I haven't put anything in to deal with redefinition of blocks (if the contents of blocks change then explosion may not be prevented properly), but this is left as an exercise for the reader

Let's define and insert a series of three blocks: LINES, ARCS and CIRCLES (no prizes for guessing which is which :-):

Inserted blocks

 

Now we run the STOPEX command on the LINES and CIRCLES blocks:

Command: STOPEX

Enter block name: circles

Command: STOPEX

Enter block name: lines

Command: EXPLODE

Select objects: all

9 found

Select objects:

Command: Specify opposite corner:

Selecting the "exploded" blocks, we see that only the ARCS blocks have actually been exploded:

Exploded blocks

 

posted on 2009-06-27 15:03  jdmei520  阅读(650)  评论(0编辑  收藏  举报

导航