Highlighting an AutoCAD entity in a nested block using .NET

I'd like to thank two people for this post: Kerry Brown, an ADN member in Australia and regular contributor to TheSwamp, pointed me to a response he'd received from a member of the DevTech team in India, Sreekar Devatha. So thanks to both Kerry and Sreekar for providing the material for this post.

The problem was to highlight the nested entity returned by the Editor.GetNestedEntity() method. Sreekar's solution was to create a sub-entity path through the nested block structure, and use that to highlight the entity in question. To create the sub-entity path he retrieved the containers of the nested entity, then reversed them and added the entity's ObjectId to the end of the list. This served as input to the constructor of the FullSubentityPath object, which could then be used in the call to Highlight() on the root container entity.

Here's the C# code:

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.Runtime;

namespace MySelectionApp

{

  public class MySelectionCmds

  {

    [CommandMethod("HLNESTED")]

    static public void HighlightNestedEntity()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      PromptNestedEntityResult rs =

        ed.GetNestedEntity("\nSelect nested entity: ");

      if (rs.Status == PromptStatus.OK)

      {

        ObjectId[] objIds = rs.GetContainers();

        ObjectId ensel = rs.ObjectId;

        int len = objIds.Length;

        // Reverse the "containers" list

        ObjectId[] revIds = new ObjectId[len + 1];

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

        {

          ObjectId id =

            (ObjectId)objIds.GetValue(len - i - 1);

          revIds.SetValue(id, i);

        }

        // Now add the selected entity to the end

        revIds.SetValue(ensel, len);

        // Retrieve the sub-entity path for this entity

        SubentityId subEnt =

          new SubentityId(SubentityType.Null, 0);

        FullSubentityPath path =

          new FullSubentityPath(revIds, subEnt);

        Transaction tr =

          doc.TransactionManager.StartTransaction();

        using (tr)

        {

          // Open the outermost container...

          ObjectId id = (ObjectId)revIds.GetValue(0);

          Entity ent =

            (Entity)tr.GetObject(id, OpenMode.ForRead);

          // ... and highlight the nested entity

          ent.Highlight(path, false);

          tr.Commit();

        }

      }

    }

  }

}

Create a nested block and run the HLNESTED command to see the code in action. You'll need to REGEN at the end for the highlighting to disappear (you would typically use ent.Unhighlight(path, false) to do this programmatically).

26 responses to “Highlighting an AutoCAD entity in a nested block using .NET”

  1. Hello Kean,

    I could not understand the need of reversing the "containers" list. Would be great if you can explain, when you have a moment.

    Thanks,
    Narayanan

  2. Hi Narayanan,

    Of course.

    Editor.GetNestedEntity() simply calls through to the ObjectARX function acedNentSel(), which - among other things - retrieves the list of containers in its 5th argument. This is then accessed by a call to PromptNestedEntityResult.GetContainers().

    Here's the documentation on PromptNestedEntityResult.GetContainers():

    >>>
    Gets an array of containers in which this entity is nested.
    Returns an array of object IDs representing the containers of this nested entity.
    <<<

    The order of this list is going "outwards" from the selected entity. If entity A is contained in block B which is nested in block C, then it will return "B, C" as the list.

    Now the FullSubentityPath is actually defined the other way: we need to get a path from the outermost entity (C) to the innermost entity (A) by reversing the list returned by GetContainers() and appending the id of the innermost entity ("C, B, A").

    FullSubentityPath is a wrapper for the ObjectARX class AcDbFullSubentPath. Here's what the ObjectARX Developer's Guide says about the array held in this object:

    >>>
    The array contains the object IDs that specify the path to the “main” entity. For example, a block reference (an entity that references a block table record) might contain two boxes, each of type AcDb3dSolid. The object ID array contains two entries: the ID of the block reference, followed by the ID of the main entity [InsertID, SolidID].
    <<<

    I hope this helps,

    Kean

  3. Thanks Kean,

    I noted down the 'Entity name' as reported by ArxDbgSnoopDb and juxtaposed it with lisp function's (nentsel) output. It made it all clear. I could have done this before!

    Thanks again,
    Narayanan

  4. Hi Kean, first sorry by my english, very god your example about AutoCAD and .NET, I would like to know How I can do to combine .NET with Visual LISP?, can you send me an example please?. I want to send data from .NET to Visual LISP, as example an Array.

    Thank you.

  5. Hi Johnny,

    I'd suggest taking a look at the following post, to see if it helps:

    keanw.com/...

    Regards,

    Kean

  6. Hey,
    I was just wondering if you would know how to count nested blocks using vba?
    Thanks

  7. Hi Kirk,

    Yes, but the apprach will be different, depending on what you mean:

    1) Allow selection of an entity, and determine the nesting of that entity in various blocks
    2) Search through the modelspace for blocks of a certain name, counting how deeply each one is nested
    3) Go through the whole modelspace and map out the nesting hierarchy of all blocks

    I'm sure there are even other interpretations... The good news is that none of these is very hard to do using any of AutoCAD's APIs.

    I'd suggest posting more specifics to one of the discussion groups - I'm sure someone there will have something for you.

    Regards,

    Kean

  8. SUBIR KUMARD DUTTA Avatar
    SUBIR KUMARD DUTTA

    Using the code sample I am able to highlight a particular entity with in a block.

    But I have a requirment where I want to highlight a particular segment with in a polyline.

    Is it possible ? Please help .

    Also please inform me how can I get the objectids & handles of the Line segments from with in a Polyline.

    Subir

  9. If it's a Polyline object (i.e. a lightweight polyline, not an old-style Polyline2d/3d) then the segments don't exist as separate objects - they are stored as coordinates, which you can retrieve and overlay with transient graphics, if you wish.

    Even heavyweight Polyline2d/3ds have separate vertex objects - not segment objects.

    Kean

  10. SUBIR KUMARD DUTTA Avatar
    SUBIR KUMARD DUTTA

    We can create temporary graphics using grdraw ( LISP ) or acedGrDraw ( ObjectARX ) . What is the method of doing so using .Net ?

    I had to highlight a particular line-segment of a closed polyline . So I have written the code segment as follows.

    ///////////////////////////////////////////////////////////////// highlight
    AcadApplication app;
    const string progID = "AutoCAD.Application.17";
    app = (Autodesk.AutoCAD.Interop.AcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject(progID);

    app.ActiveDocument.SendCommand("cmdecho 0" + "\n");
    app.ActiveDocument.SendCommand("regen" + "\n");
    app.ActiveDocument.SendCommand("redraw" + "\n");

    String ST = Rooms.Text;
    ST = ST.Replace("Edge", "");
    ST = ST.Trim();

    int i = System.Convert.ToInt16(ST);
    int ii = i;

    if (i == pLine.NumberOfVertices)
    ii = 0;

    double x1, y1, x2, y2;

    Point2d p1 = pLine.GetPoint2dAt(i-1);
    Point2d p2 = pLine.GetPoint2dAt(ii);

    x1 = p1.X;
    y1 = p1.Y;

    x2 = p2.X;
    y2 = p2.Y;

    string st1, st2,st3;

    st1 = "(setq p1 (list " + x1.ToString().Trim() + " " + y1.ToString().Trim() + " )) ";
    st2 = "(setq p2 (list " + x2.ToString().Trim() + " " + y2.ToString().Trim() + " )) ";
    st3 = "(grdraw p1 p2 1) ";

    But I myself is not feeling satisfied though it worked fine for my current purpose.

    I want to know , is there any other method to achive this using pure .Net and not calling any LISP.

    app.ActiveDocument.SendCommand(st1);
    app.ActiveDocument.SendCommand(st2);
    app.ActiveDocument.SendCommand(st3);
    ///////////////////////////////////////////////////////////////// highlight

  11. Editor.DrawVector(s) or the Transient Graphics API introduced in AutoCAD 2009.

    Kean

  12. Hi Kean,

    If I want to highlight some entities as red color, which parameters should I change? Because your example only making the highlighted entities become dashed line type.

    Cheers,

    iis

  13. Hi iis,

    There isn't really a parameter to change, as such... with the call to ent.Highlight() we use the standard AutoCAD highlighting mechanism. You could probably use the transient graphics API (as shown in this post) to create transient graphics for the sub-entity, instead, but that will take some work.

    Kean

  14. hi

    how can i get an object crated by SendCommand

  15. Dear Mr Kean,
    I have some Attribute Blocks in my drawings. These block contain attributes and text. Please help me the coding to return the attribute value and text string. Thank any way.
    regard,
    nk_long

  16. Kean Walmsley Avatar

    Dear nk_long,

    This isn't a support forum: please post your questions to the AutoCAD .NET Discussion Group or to the ADN team.

    That said, this post may be of some help:

    keanw.com/2007/07/updating-a-spec.html

    Regards,

    Kean

  17. Kean Walmsley Avatar

    Wow - just saw this comment 2 years later (it must have gone to my Junk list).

    I don't provide support in this way, but I suggest considering using something like (entlast) to get a recently created object (you can even store the latest handle before running the command, and get all subsequent ones).

    Kean

  18. Sorry, and really thank for your information.
    Regard,
    Nk_long

  19. Is there any way to select multiple nested entities by specifying rectangle area using mouse (standard selection)?

    Thanks.

  20. Not that I'm aware of. You might try posting this question via ADN or the AutoCAD .NET Discussion Group.

    Kean

  21. Hello Mr Walmsley,

    I want to do about the same thing as in this blog, except that the user should not select the nested block, but it will be done programmatically.
    So instead of the "PromptNestedEntityResult" object I have a "BlockReference".

    I assume it is really easy, but I have NO idea how to get the "containers" (rs.GetContainers();) for a BlockReference. Have you any idea?

    Thank you for the great blogs.

  22. You should be able to go get the OwnerId() iteratively until you hit either the model- or a paperspace block table record.

    Kean

  23. Hello Kean,
    It works fine for everything except nested attribute reference.Cound you take a look at it?

    Thanks
    Jian

    1. Hi Jian,

      If you're interested in additional capabilities, please post a request on the AutoCAD .NET forum.

      Regards,

      Kean

  24. Hi Kean,
    Greetings for the Day!
    I have list of Entities show below.
    List<entity> lstpLine = new List<entity>();
    Could you please help me to highlight them as selected mode using c#.Net, The same i should present to user.
    May i get your assistance?

    Regards

    Alex Andrews.

    1. Hi Alex,

      You should search this blog (and the internet) for "pickfirst selection". That should get you what you need.

      Failing that, please post your question to the AutoCAD .NET forum: someone there will be able to help.

      Best,

      Kean

Leave a Reply

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