Using a jig from .NET to multiply insert AutoCAD blocks - Part 2

In the last post we looked at a jig that can be used to add block references to an AutoCAD drawing. This post extends that code to support annotative block definitions (available from AutoCAD 2008) and blocks with attributes. Thanks once again to Holger Steiner for the jig class and to Roland Feletic for posting the code to support annotative blocks.

A comment on the previous post asked about having attributes visible during the jig process: unfortunately that's not currently possible, as the existing managed AttributeCollection implementation wraps the version of the AcDbBlockReference::appendAttribute() ObjectARX function that requires the block reference to already have been added to the drawing. So for now you will need to live with the fact that the attributes don't display during the jig, but do display as soon as the block has been added to the drawing.

Below is the modified C# code, with line numbers. And here's the source file for download.

    1 using Autodesk.AutoCAD.ApplicationServices;

    2 using Autodesk.AutoCAD.DatabaseServices;

    3 using Autodesk.AutoCAD.EditorInput;

    4 using Autodesk.AutoCAD.Runtime;

    5 using Autodesk.AutoCAD.Geometry;

    6 using Autodesk.AutoCAD.Internal;

    7

    8 namespace BlockJigTest

    9 {

   10   class BlockJig : EntityJig

   11   {

   12     Point3d mCenterPt, mActualPoint;

   13

   14     public BlockJig(BlockReference br)

   15       : base(br)

   16     {

   17       mCenterPt = br.Position;

   18     }

   19

   20     protected override SamplerStatus Sampler(JigPrompts prompts)

   21     {

   22       JigPromptPointOptions jigOpts =

   23         new JigPromptPointOptions();

   24       jigOpts.UserInputControls =

   25         (UserInputControls.Accept3dCoordinates

   26         | UserInputControls.NoZeroResponseAccepted

   27         | UserInputControls.NoNegativeResponseAccepted);

   28

   29       jigOpts.Message =

   30         "\nEnter insert point: ";

   31

   32       PromptPointResult dres =

   33         prompts.AcquirePoint(jigOpts);

   34

   35       if (mActualPoint == dres.Value)

   36       {

   37         return SamplerStatus.NoChange;

   38       }

   39       else

   40       {

   41         mActualPoint = dres.Value;

   42       }

   43       return SamplerStatus.OK;

   44     }

   45

   46     protected override bool Update()

   47     {

   48       mCenterPt = mActualPoint;

   49       try

   50       {

   51         ((BlockReference)Entity).Position = mCenterPt;

   52       }

   53       catch (System.Exception)

   54       {

   55         return false;

   56       }

   57       return true;

   58     }

   59

   60     public Entity GetEntity()

   61     {

   62       return Entity;

   63     }

   64   }

   65

   66   public class Commands

   67   {

   68     [CommandMethod("BJIG")]

   69     public void CreateBlockWithJig()

   70     {

   71       Document doc =

   72         Application.DocumentManager.MdiActiveDocument;

   73       Database db = doc.Database;

   74       Editor ed = doc.Editor;

   75

   76       // First let's get the name of the block

   77       PromptStringOptions opts =

   78         new PromptStringOptions("\nEnter block name: ");

   79       PromptResult pr = ed.GetString(opts);

   80       if (pr.Status == PromptStatus.OK)

   81       {

   82         Transaction tr =

   83           doc.TransactionManager.StartTransaction();

   84         using (tr)

   85         {

   86           // Then open the block table and check the

   87           // block definition exists

   88           BlockTable bt =

   89             (BlockTable)tr.GetObject(

   90               db.BlockTableId,

   91               OpenMode.ForRead

   92             );

   93           if (!bt.Has(pr.StringResult))

   94           {

   95             ed.WriteMessage("\nBlock not found.");

   96           }

   97           else

   98           {

   99             ObjectId bdId = bt[pr.StringResult];

  100

  101             // We loop until the jig is cancelled

  102             while (pr.Status == PromptStatus.OK)

  103             {

  104               // Create the block reference and

  105               // add it to the jig

  106               Point3d pt = new Point3d(0, 0, 0);

  107               BlockReference br =

  108                 new BlockReference(pt, bdId);

  109

  110               BlockJig entJig = new BlockJig(br);

  111

  112               // Perform the jig operation

  113               pr = ed.Drag(entJig);

  114               if (pr.Status == PromptStatus.OK)

  115               {

  116                 // If all is OK, let's go and add the

  117                 // entity to the modelspace

  118                 BlockTableRecord ms =

  119                   (BlockTableRecord)tr.GetObject(

  120                     bt[BlockTableRecord.ModelSpace],

  121                     OpenMode.ForWrite

  122                   );

  123                 ms.AppendEntity(

  124                   entJig.GetEntity()

  125                 );

  126                 tr.AddNewlyCreatedDBObject(

  127                   entJig.GetEntity(),

  128                   true

  129                 );

  130

  131                 // Start attrib/annot-scale support code

  132                 BlockTableRecord bd =

  133                   (BlockTableRecord)tr.GetObject(

  134                     bdId,

  135                     OpenMode.ForRead

  136                   );

  137                 if (bd.Annotative == AnnotativeStates.True)

  138                 {

  139                   ObjectContextManager ocm =

  140                     db.ObjectContextManager;

  141                   ObjectContextCollection occ =

  142                     ocm.GetContextCollection(

  143                       "ACDB_ANNOTATIONSCALES"

  144                     );

  145                   ObjectContexts.AddContext(

  146                     br,

  147                     occ.CurrentContext

  148                   );

  149                 }

  150

  151                 // Add the attributes

  152                 foreach (ObjectId attId in bd)

  153                 {

  154                   Entity ent =

  155                     (Entity)tr.GetObject(

  156                       attId,

  157                       OpenMode.ForRead

  158                     );

  159                   if (ent is AttributeDefinition)

  160                   {

  161                     AttributeDefinition ad =

  162                       (AttributeDefinition)ent;

  163                     AttributeReference ar =

  164                       new AttributeReference();

  165                     ar.SetAttributeFromBlock(

  166                       ad,

  167                       br.BlockTransform

  168                     );

  169                     br.AttributeCollection.AppendAttribute(ar);

  170                     tr.AddNewlyCreatedDBObject(ar, true);

  171                   }

  172                 }

  173                 // End attrib/annot-scale support code

  174

  175                 // Call a function to make the graphics display

  176                 // (otherwise it will only do so when we Commit)

  177                 doc.TransactionManager.QueueForGraphicsFlush();

  178               }

  179             }

  180           }

  181           tr.Commit();

  182         }

  183       }

  184     }

  185   }

  186 }

You'll notice the lines in red, from lines 131 to 173, have been added to the previous version of the code. The lines from 137 to 149 will only work with versions since AutoCAD 2008, as they're related to annotation scaling. As mentioned previously, this section of code relies on AcMgdInternal.dll, an unsupported assembly which is liable to change in future releases.

Update:

Subsequent to the comment from Roland, I've modified the above code to attach the annotation scale earlier, before the jig starts. This allows the jig to properly represent the block being placed (as otherwise annotative blocks will not display during the jig operation):

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Internal;

namespace BlockJigTest

{

  class BlockJig : EntityJig

  {

    Point3d mCenterPt, mActualPoint;

    public BlockJig(BlockReference br)

      : base(br)

    {

      mCenterPt = br.Position;

    }

    protected override SamplerStatus Sampler(JigPrompts prompts)

    {

      JigPromptPointOptions jigOpts =

        new JigPromptPointOptions();

      jigOpts.UserInputControls =

        (UserInputControls.Accept3dCoordinates

        | UserInputControls.NoZeroResponseAccepted

        | UserInputControls.NoNegativeResponseAccepted);

      jigOpts.Message =

        "\nEnter insert point: ";

      PromptPointResult dres =

        prompts.AcquirePoint(jigOpts);

      if (mActualPoint == dres.Value)

      {

        return SamplerStatus.NoChange;

      }

      else

      {

        mActualPoint = dres.Value;

      }

      return SamplerStatus.OK;

    }

    protected override bool Update()

    {

      mCenterPt = mActualPoint;

      try

      {

        ((BlockReference)Entity).Position = mCenterPt;

      }

      catch (System.Exception)

      {

        return false;

      }

      return true;

    }

    public Entity GetEntity()

    {

      return Entity;

    }

  }

  public class Commands

  {

    [CommandMethod("BJIG")]

    public void CreateBlockWithJig()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

      // First let's get the name of the block

      PromptStringOptions opts =

        new PromptStringOptions("\nEnter block name: ");

      PromptResult pr = ed.GetString(opts);

      if (pr.Status == PromptStatus.OK)

      {

        Transaction tr =

          doc.TransactionManager.StartTransaction();

        using (tr)

        {

          // Then open the block table and check the

          // block definition exists

          BlockTable bt =

            (BlockTable)tr.GetObject(

              db.BlockTableId,

              OpenMode.ForRead

            );

          if (!bt.Has(pr.StringResult))

          {

            ed.WriteMessage("\nBlock not found.");

          }

          else

          {

            ObjectId bdId = bt[pr.StringResult];

            // We loop until the jig is cancelled

            while (pr.Status == PromptStatus.OK)

            {

              // Create the block reference and

              // add it to the jig

              Point3d pt = new Point3d(0, 0, 0);

              BlockReference br =

                new BlockReference(pt, bdId);

              // Start annot-scale support code

              BlockTableRecord bd =

                (BlockTableRecord)tr.GetObject(

                  bdId,

                  OpenMode.ForRead

                );

              // Using will dispose of the block definition

              // when no longer needed

              using (bd)

              {

                if (bd.Annotative == AnnotativeStates.True)

                {

                  ObjectContextManager ocm =

                    db.ObjectContextManager;

                  ObjectContextCollection occ =

                    ocm.GetContextCollection(

                      "ACDB_ANNOTATIONSCALES"

                    );

                  ObjectContexts.AddContext(

                    br,

                    occ.CurrentContext

                  );

                }

              }

              // End annot-scale support code

              BlockJig entJig = new BlockJig(br);

              // Perform the jig operation

              pr = ed.Drag(entJig);

              if (pr.Status == PromptStatus.OK)

              {

                // If all is OK, let's go and add the

                // entity to the modelspace

                BlockTableRecord ms =

                  (BlockTableRecord)tr.GetObject(

                    bt[BlockTableRecord.ModelSpace],

                    OpenMode.ForWrite

                  );

                ms.AppendEntity(

                  entJig.GetEntity()

                );

                tr.AddNewlyCreatedDBObject(

                  entJig.GetEntity(),

                  true

                );

                // Start attribute support code

                bd =

                  (BlockTableRecord)tr.GetObject(

                    bdId,

                    OpenMode.ForRead

                  );

                // Add the attributes

                foreach (ObjectId attId in bd)

                {

                  Entity ent =

                    (Entity)tr.GetObject(

                      attId,

                      OpenMode.ForRead

                    );

                  if (ent is AttributeDefinition)

                  {

                    AttributeDefinition ad =

                      (AttributeDefinition)ent;

                    AttributeReference ar =

                      new AttributeReference();

                    ar.SetAttributeFromBlock(

                      ad,

                      br.BlockTransform

                    );

                    br.AttributeCollection.AppendAttribute(ar);

                    tr.AddNewlyCreatedDBObject(ar, true);

                  }

                }

                // End attribute support code

                // Call a function to make the graphics display

                // (otherwise it will only do so when we Commit)

                doc.TransactionManager.QueueForGraphicsFlush();

              }

            }

          }

          tr.Commit();

        }

      }

    }

  }

}

Update 2:

This more recent post shows how to jig blocks with attributes.

13 responses to “Using a jig from .NET to multiply insert AutoCAD blocks - Part 2”

  1. Roland Feletic Avatar
    Roland Feletic

    Thank you Kean for this code.
    I was searching so long for for this one: "doc.TransactionManager.QueueForGraphicsFlush();"

  2. Roland Feletic Avatar
    Roland Feletic

    Hi Kean,
    I had some code like yours and changed it now that it works with annotative blocks. I just want to say that it is not possible to use the jig-example with annotative-blocks. It is not possible to jig annotative-blocks, well, it is possible but you will not see them. Is there any chance to add the annotation-scale during the jig?

    Roland

  3. Kean Walmsley Avatar

    Thanks, Roland. Hopefully the above code now fixes it...

    Kean

  4. Roland Feletic Avatar
    Roland Feletic

    Thank you, Kean, this new code works really perfect.

    Roland

  5. Roland Feletic Avatar
    Roland Feletic

    Hi Kean,
    just one thing I miss in AutoCAD (eg. measure) and in this program is the support of the units of the block. Therefore I would change the code a little bit and insert two more lines.

    using (bd)
    {
    if (bd.Annotative == AnnotativeStates.True)
    {
    ObjectContextManager ocm = db.ObjectContextManager;
    ObjectContextCollection occ = ocm.GetContextCollection("ACDB_ANNOTATIONSCALES");
    ObjectContexts.AddContext(br, occ.CurrentContext);
    }
    else
    br.ScaleFactors = new Scale3d(br.UnitFactor);
    }

    Roland

  6. Kean Walmsley Avatar

    Thanks, Roland!

    Kean

  7. Hi Kean

    Is it possible to append attributes to the blockref before drag-operation? I get an "eNoDatabase" error when i try. I know the object isn't added to the DB until after drag.

    Cheers
    Tore

  8. Kean Walmsley Avatar

    Hi Tore,

    Unfortunately not right now: AppendAttribute() wraps the version of the ObjectARX function that requires a db-resident blockref.

    I'm hopeful that this will be enabled in a future release, but for now you're stuck with the approach I've shown here, I'm afraid.

    Regards,

    Kean

  9. Hi Kean,

    How do I run this code when a button on a form is clicked?

    Thanks
    shers

  10. Hi Shers,

    It depends on whether you want to create a modal or modeless dialog. There should be examples on the ObjectARX SDK (under samples/dotNet) that show how to create dialogs and put code behind buttons.

    Regards,

    Kean

  11. I tried to start really simple and just insert one block at (0,0) so that I could see it. I am getting the msg printed "(2126748928)Temperature Switch - Imperial inserted" on my command line but do not see the block (which I took from AutoCAD 2009/Samples/Dynamic Blocks)

    Anyone have any ideas on why this code will not work. Block is loaded into the block table.

    Thanks in advance

    using System;

    using Autodesk.AutoCAD;

    using Autodesk.AutoCAD.Runtime;

    using Autodesk.AutoCAD.Geometry;

    using Autodesk.AutoCAD.ApplicationServices;

    using Autodesk.AutoCAD.DatabaseServices;

    using Autodesk.AutoCAD.EditorInput;

    using System.Collections.Generic;
    using System.IO;

    namespace BlockImport
    {

    public class BlockImportClass
    {

    [CommandMethod("BI")]
    public void InsertBlock()
    {
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Database db = doc.Database;
    Editor ed = doc.Editor;
    try
    {

    //Test block name and point (final version will use user input)
    string nameOfBlock = "Temperature Switch - Imperial";
    Autodesk.AutoCAD.Geometry.Point3d pos = new Point3d(0, 0, 0);
    // Get the current working database
    Transaction tr = db.TransactionManager.StartTransaction();
    // Then open the block table and check the
    // block definition exists
    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
    if (!bt.Has(nameOfBlock))
    {
    ed.WriteMessage("\nBlock not found.");
    }
    else
    {
    ObjectId bdId = bt[nameOfBlock];
    Point3d pt = new Point3d(0, 0, 0);
    // Create the block reference
    BlockReference br = new BlockReference(pt, bdId);
    //Add block reference to model space table
    BlockTableRecord btrms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
    btrms.AppendEntity(br);
    tr.AddNewlyCreatedDBObject(br, true);
    //Graphics Flush so block will be visible
    doc.TransactionManager.QueueForGraphicsFlush();
    ed.WriteMessage(bdId + nameOfBlock + " inserted");
    }

    //Clean up transaction

    tr.Commit();
    tr.Dispose();
    }
    catch (Autodesk.AutoCAD.Runtime.Exception ex)
    {
    ed.WriteMessage("\nError during copy: " + ex.Message);
    }
    }

    }

    }

  12. looks like it may be a problem with dynamic blocks? I just tested it with a standard block and had no trouble. Any ideas on how to use it for dynamic blocks?

    Maybe just posting my code was enough therapy to try a new approach haha

  13. It should work fine for dynamic blocks, too (just scanning your code). Please submit your question via the ADN site (if you're a member), or on the AutoCAD .NET Discussion Group. Someone there will be able to help.

    Kean

Leave a Reply to Paul Cancel reply

Your email address will not be published. Required fields are marked *