Kean Walmsley


  • About the Author
    Kean on Google+

April 2014

Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      







« "Through the Interface" has a new look | Main | Master list of "Through the Interface" posts »

August 18, 2008

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

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83452464869e200e553f016f08833

Listed below are links to weblogs that reference Preventing an AutoCAD block from being exploded using .NET:

blog comments powered by Disqus

10 Random Posts