Creating an AutoCAD block using .NET

This post – and its sister post, which will cover creating a group – are further topics I would expect to have already covered in this blog at some point, or perhaps to see covered by the AutoCAD .NET Developer's Guide (and I'm sure they will be in a future incarnation of it). So thanks to Adam Nagy, from DevTech EMEA, for suggesting these topics. It's nice to go back to basics once in a while (although this means I do have to beg the patience of the readers out there who know this stuff inside out).

So, here's some simple C# code that creates a simple block definition (a BlockTableRecord in the BlockTable) and a corresponding block insertion (a BlockReference in the model-space BlockTableRecord).

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

 

namespace CollectionCreation

{

  public class Commands

  {

    [CommandMethod("CB")]

    public void CreateBlock()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        // Get the block table from the drawing

 

        BlockTable bt =

          (BlockTable)tr.GetObject(

            db.BlockTableId,

            OpenMode.ForRead

          );

 

        // Check the block name, to see whether it's

        // already in use

 

        PromptStringOptions pso =

          new PromptStringOptions(

            "\nEnter new block name: "

          );

        pso.AllowSpaces = true;

 

        // A variable for the block's name

 

        string blkName = "";

 

        do

        {

          PromptResult pr = ed.GetString(pso);

 

          // Just return if the user cancelled

          // (will abort the transaction as we drop out of the using

          // statement's scope)

 

          if (pr.Status != PromptStatus.OK)

            return;

 

          try

          {

            // Validate the provided symbol table name

 

            SymbolUtilityServices.ValidateSymbolName(

              pr.StringResult,

              false

            );

 

            // Only set the block name if it isn't in use

 

            if (bt.Has(pr.StringResult))

              ed.WriteMessage(

                "\nA block with this name already exists."

              );

            else

              blkName = pr.StringResult;

          }

          catch

          {

            // An exception has been thrown, indicating the

            // name is invalid

 

            ed.WriteMessage(

              "\nInvalid block name."

            );

          }

 

        } while (blkName == "");

 

     
60; 
// Create our new block table record...

 

        BlockTableRecord btr = new BlockTableRecord();

 

        // ... and set its properties

 

        btr.Name = blkName;

 

        // Add the new block to the block table

 

        bt.UpgradeOpen();

        ObjectId btrId = bt.Add(btr);

        tr.AddNewlyCreatedDBObject(btr, true);

 

        // Add some lines to the block to form a square

        // (the entities belong directly to the block)

 

        DBObjectCollection ents = SquareOfLines(5);

        foreach (Entity ent in ents)

        {

          btr.AppendEntity(ent);

          tr.AddNewlyCreatedDBObject(ent, true);

        }

 

        // Add a block reference to the model space

 

        BlockTableRecord ms =

          (BlockTableRecord)tr.GetObject(

            bt[BlockTableRecord.ModelSpace],

            OpenMode.ForWrite

          );

 

        BlockReference br =

          new BlockReference(Point3d.Origin, btrId);

 

        ms.AppendEntity(br);

        tr.AddNewlyCreatedDBObject(br, true);

 

        // Commit the transaction

 

        tr.Commit();

 

        // Report what we've done

 

        ed.WriteMessage(

          "\nCreated block named \"{0}\" containing {1} entities.",

          blkName, ents.Count

        );

      }

    }

 

    private DBObjectCollection SquareOfLines(double size)

    {

      // A function to generate a set of entities for our block

 

      DBObjectCollection ents = new DBObjectCollection();

      Point3d[] pts =

          { new Point3d(-size, -size, 0),

            new Point3d(size, -size, 0),

            new Point3d(size, size, 0),

            new Point3d(-size, size, 0)

          };

      int max = pts.GetUpperBound(0);

 

      for (int i = 0; i <= max; i++)

      {

        int j = (i == max ? 0 : i + 1);

        Line ln = new Line(pts[i], pts[j]);

        ents.Add(ln);

      }

      return ents;

    }

  }

}

Let's see what happens when we run the CB command to create a block:

Command: CB

Enter new block name: Square

Created block named "Square" containing 4 entities.

That's really all there is to it: after the code has been run you'll see a block (named "Square") made up of four lines aligned in a square around the origin.

I might have separated the above code out into functions to create the block definition and the block reference, but my main goal for this post was to show the procedure in a linear fashion. I did use one separate function – as we'll also use that to create our group in the next post – which creates a collection of objects that will make up our block.

43 responses to “Creating an AutoCAD block using .NET”

  1. Here's one that could probably do with a makeover 🙂
    1
    (Need to be a member to see attachments, though anyone can read.)

    Kerry Brown

  2. Hı KeAN also we wonder
    How may we insert with attribute block reference from dwg file
    I search this but I found anything in the AutoCAD .NET Developer’s Guide
    Best regards lots of thanks for yu

  3. Hi Helen,

    The code in this post should be of help.

    Regards,

    Kean

  4. Kean,

    I was wondering if you had any examples of blocks with reactors. Specifically, I am looking for something like a block consisting of a line with various length attributes in text format displayed with the line. I would like those length attributes that are displayed to update with stretch, grip edit, basically any change in length to the line. Any direction via posts you could give would be greatly appreciated. Thanks.

    Dave Sparks

  5. David,

    This is an ideal use-case for the Overrule API, which was introduced in AutoCAD 2010. I really wouldn't want to try to use reactors to maintain text as a line is edited - that would be very hard to do well - but Overrules allow you to add text during the display of a line (you could check for XData so it didn't happen for all lines, for instance).

    I can show how to do this in a future post, but a version that would work prior to 2010 is off the table, I'm afraid (It'd just be too much work).

    Regards,

    Kean

  6. Hello again Kean, not sure if you check posts this old but I thought it was worth a shot. I was wondering how do I go about removing these blocks or editing them. I need to create 5 blocks, and then if a 6th is created they need to essentially all overwrite/replace each other and the first would be deleted to make room for the new. I am running into issues with the systemTable because of duplicate names and I can't figure out how to remove them.
    Thanks for your help, hope you see this!

  7. Kean Walmsley Avatar

    Instead of checking whether a block exists (using bt.Has()) and printing a warning message, if so, you could open it using bt[pr.StringResult] to get the ObjectId of that block, open it for write and then do what you want with it.

    Alternatively you could call Erase() on that block and create a new one.

    Kean

  8. Dimitrina Dimitrova Avatar
    Dimitrina Dimitrova

    Hello Kean, do you know if there is any way to remove existing list of allowed values of a property in a blockreference (AutoCAD 2010, .Net API)? I mean in the BlockDefinition and also in the created instances in the drawing. Or to change the PropertyTypeCode of the property - it is exposed as readOnly.
    Thanks a lot, it is urgently and I am not sure... 🙂
    Dimitrina

  9. Hello Dimitrina,

    I'm afraid I really don't have time to respond to support requests.

    Your comment doesn't appear to relate to this post, so please submit your question to the ADN team, if you're a member, or otherwise the AutoCAD .NET Discussion Group.

    Regards,

    Kean

  10. Hi Kean, I tried the example that you provided to make a block. It run and show error when I give Block name & press enter. The message is : Unhandled Exception has occurred...... eLockVoilation...

    Autodesk.AutoCAD.Runtime.Exception: eLockViolation Autodesk.AutoCAD.DatabaseServices.DBObject.UpgradeOpen()

    Please help me out, Many thanks.

  11. Hi Kean
    i need some help about removing the blocks from block table if the block name already exists. something like this...
    if blocktable.has(blkname)
    {
    blocktable.delete(blkname); // method not implemented in the blocktable object here.
    }
    else
    {
    //create new block
    }

  12. Hi Bizay,

    This is not a forum for support.

    In future, please submit your questions to the ADN team, if you're a member, or otherwise the AutoCAD .NET Discussion Group.

    In this case I suggest looking at opening the block for write (probably using UpgradeOpen() after confirming it is there) and calling Erase().

    Kean

  13. Is possible to modify an existing block, without deleting the existing one and adding a new one with the modified proprieties?

  14. Absolutely.

    Kean

  15. Kean, I've posted a topic with this problem on the ADN forums ( forums.autodesk.com/t5/NET/How-to-modify-the-components-of-a-BlockReference/td-p/2915380 ). Do you have any idea what I'm doing wrong?

  16. Hopefully, this is onTopic:

    Probably, you don't have the time for this, so could you tell in a few lines what should I do?

    What I'm doing:

    I explode the BlockReference, using a DBObjectCollection variable, but when I try to change the proprieties, I don't see anything changed in the drawing. What I'm doing wrong.

  17. The results of Explode() are not automatically added to the model-space, you will need to do so yourself.

    Put another way, in ObjectARX and .NET does not work in exactly the same as the EXPLODE command: just like Database.Purge(), it's a non-destructive version of the operation.

    The AutoCAD .NET Developer's Guide should help.

    Kean

  18. how to insert text in all selected objects using vba or lisp.

  19. Sorry - this isn't a forum for support. Please post your question via ADN or the discussion groups.

    Kean

  20. It works well with AutoCAD 2011 but why can't I see the new block in the menue Insert->Block (in german 'Einfügen->Block', hope the menue is similar in english)

  21. I'm not sure which menu you mean. If you mean the pull-down in the INSERT dialog box, then it should be in there. If there's some kind of MRU (most recently used) list that I don't know about (which is quite possible) then this is another question.

    Kean

  22. I found what I was looking for on another page of your very helpful blog

    keanw.com/2011/01/combining-autocad-blocks-in-separate-files-into-a-single-dwg-using-net-take-2.html

    with little modification it works perfect for me

  23. Great - glad to hear it. 🙂

    Kean

  24. Can you create a dynamic block? Or can we analyse the actions of an existing dynamic block definition?

    For example, let's say we have a dynamic block with an array action on a line. I would like to access the array action to retrieve the id of the line or to change the offset of the array in a reference of that block definition.

  25. You can certainly anaylyse an existing dynamic block definition, although I don't believe you can currently define a dynamic block (I may be mis-remembering, though - best to ask on the forums).

    These posts may be helpful:

    keanw.com/2009/03/accessing-the-properties-of-a-dynamic-autocad-block-using-net.html
    keanw.com/2009/03/painting-properties-between-dynamic-autocad-blocks-using-net.html

    Kean

  26. Gerrit van Diepen Avatar
    Gerrit van Diepen

    Hi Kean,

    It is possible to set the orientation of an existing block to match the block orientation to the layout.
    This can be done if the block is an annotative block.

    But how do I set this property in the blocktablerecord / blockreference?
    Can this also if the block is not an annotative block?

    Any example of code in VB.net or C# would help

    Gerrit

  27. Hi Gerrit,

    Sorry - I'm not sure I understand exactly what you mean.

    Perhaps you can create a thread on the discussion forum including a DWG and/or some screenshots, and post the link here?

    Regards,

    Kean

  28. Gerrit van Diepen Avatar
    Gerrit van Diepen

    Hi Kean,

    Here is the link
    forums.autodesk.com/t5/NET/Match-block-orientating-to-layout/td-p/3223508

    I hope you can see my problem now ...

    Regards,

    Gerrit

  29. Hi !
    i'm student in VietNam.
    I'm researching ObjectARX with VB.Net and i feel so amaze ^^
    Now, i have study with "AutoCAD .NET developer's Guide" document.And i have a problem.
    I create a procedure to draw some device, i wonder how i can create new object same Circle and Line in AutoCAD( or similar create a Block )?
    Regard,
    Cuong.
    p.s
    Email : cuongxda@gmail.com

  30. Hi Cường,

    This isn't a support forum, I'm afraid.

    I suggest posting your question - with a bt more detail on what you're looking for - to the AutoCAD .NET Discussion Group. Someone there will be able to help, I'm sure.

    Regards,

    Kean

  31. Hi Kean and readers,

    In this post, we have created a blocktable record, and we are adding some entities to the blocktable record (a series of lines). This is the line that confuses me:

    BlockReference br = new BlockReference(Point3d.Origin, btrId);

    why do we have to create a block reference? We can add the lines to modelspace without them can't we?

    ObjectARX help files says: "BlockReference Class: The BlockReference class represents the INSERT entity within AutoCAD. A block reference is used to place, size, and display an instance of the collection of entities within the BlockTableRecord that it references."

    We we have already defined where all the lines are going? I'm confused.

    any clarifications much appreciated.

    rgds
    Ben

    1. Kean Walmsley Avatar
      Kean Walmsley

      Hi Ben,

      I'm not sure why you'd want to add the lines to modelspace if they're already in another block table record. What exactly is your goal, here?

      Regards,

      Kean

  32. I respect you.
    Always thank you.
    I want Selected Block Source Code.
    PromptSelectionResult//
    Please make me the source code..please
    dltndyd1490@gmail.com

    1. You want to allow people to select a block? There are many examples of selection using Editor.GetSelection() on this blog... searching for "AddAllowedClass" should show examples of limiting the user to selecting specific object types.

      Kean

  33. Gabriel Potestades Avatar
    Gabriel Potestades

    Hi Kean,

    I tried adding all objects in a drawing to a single block. The block is being registered in the Block Table but it isn't added in the model space. In short, I want to imitate the BLOCK command where you select objects and they are instantly added to the model space as a block. Am I missing a function?

  34. Kean, this is an old post, but I have a difficult question I have been unable to determine.
    Like you I am creating a block insert at the time of code, but I also want to insert a block within the block I am creating. I can append entities such as text etc to the BlockTableRecord(btr), but how on earth do you append an inserted block to the btr?

    1. Kean Walmsley Avatar

      HI Brett,

      You need to create a BlockReference and append it - just as you would any other entity - to the parent BlockTableRecord.

      Regards,

      Kean

  35. Hello Kean Walmsley,

    I'm using similar code as your post, and am trying to understand it.

    In your code, aren't you suppose to dispose the variable "br" (BlockReference br = new BlockReference(Point3d.Origin, btrId);)?
    If not, can you briefly explain why this isn't required in your code?

    "You need to dispose of an object under the following conditions:
    - Always with a Transaction or DocumentLock object.
    - Always with newly created database objects, objects derived from DBObject, that are being added to a transaction.
    - Always with newly created database objects, objects derived from DBObject, that are not added to the database.
    - Do not have to with existing database objects, objects derived from DBObject, opened with a transaction object and the GetObject method."

    Thank you.

    1. Kean Walmsley Avatar

      Hello Qwerta,

      Once a newly created database object has been added to a transaction, there's no need to dispose of it manually: the transaction manages it from there on out.

      If the information you've quoted is from official documentation, it should probably be corrected.

      Regards,

      Kean

      1. Hello Kean,

        It is quoted from:

        docs.autodesk.com/AC...

        or:

        knowledge.autodesk....

        I will try to find some documentation on how and what a transaction disposes.

        Thank you for the response.

  36. Hello Kean,

    How can we update the color of the square of lines if we have to?

    1. Kean Walmsley Avatar

      Of course. If you need help understanding how, please post questions to the AutoCAD .NET forum.

      Kean

      1. forums.autodesk.com...

        Kean, I have posted my question, in the above link.

Leave a Reply to Gabriel Potestades Cancel reply

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