Kean Walmsley

July 2009

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 31  

Twitter Updates

    follow me on Twitter



    « Using a jig from .NET to multiply insert AutoCAD blocks | Main | Displaying a progress meter during long operations in AutoCAD using .NET »

    May 22, 2007

    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.

    TrackBack

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

    Listed below are links to weblogs that reference Using a jig from .NET to multiply insert AutoCAD blocks - Part 2:

    Comments

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

    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

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

    Kean

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

    Roland

    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

    Thanks, Roland!

    Kean

    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

    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

    Hi Kean,

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

    Thanks
    shers

    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

    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);
    }
    }

    }

    }

    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

    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

    Verify your Comment

    Previewing your Comment

    This is only a preview. Your comment has not yet been posted.

    Working...
    Your comment could not be posted. Error type:
    Your comment has been posted. Post another comment

    The letters and numbers you entered did not match the image. Please try again.

    As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

    Having trouble reading this image? View an alternate.

    Working...

    Post a comment

    Feed & Share

    Search