Finding all the AutoCAD entities on a particular layer using .NET

This question came in recently by email:

Is there a way to obtain the object IDs of all the object on a layer? I found the GetAllObjects() method of the Transaction object but it doesn't work.

That's right: GetAllObjects() will return the objects accessed via (or added to) the transaction - it has nothing to do with retrieving objects based on any particular property.

What you need to do is Editor.SelectAll(), the equivalent of acedSSGet("X") in ObjectARX and (ssget "X") in AutoLISP. You need the version where you pass in a SelectionFilter specifying the layer for which to search.

Here's some C# code doing just this, with the hard work encapsulated in a function (GetEntitiesOnLayer()) to make it easier to integrate into your code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

namespace EntitySelection

{

  public class Commands

  {

    [CommandMethod("EOL")]

    static public void EntitiesOnLayer()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      PromptResult pr =

        ed.GetString("\nEnter name of layer: ");

      if (pr.Status == PromptStatus.OK)

      {

        ObjectIdCollection ents =

          GetEntitiesOnLayer(pr.StringResult);

        ed.WriteMessage(

          "\nFound {0} entit{1} on layer {2}",

          ents.Count,

          (ents.Count == 1 ? "y" : "ies"),

          pr.StringResult

        );

      }

    }

    private static ObjectIdCollection

      GetEntitiesOnLayer(string layerName)

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      // Build a filter list so that only entities

      // on the specified layer are selected

      TypedValue[] tvs =

        new TypedValue[1] {

            new TypedValue(

              (int)DxfCode.LayerName,

              layerName

            )

          };

      SelectionFilter sf =

        new SelectionFilter(tvs);

      PromptSelectionResult psr =

        ed.SelectAll(sf);

      if (psr.Status == PromptStatus.OK)

        return

          new ObjectIdCollection(

            psr.Value.GetObjectIds()

          );

      else

        return new ObjectIdCollection();

    }

  }

}

Running EOL simply counts the entities on a particular layer (the following is from running in the 3D House.dwg sample drawing):

Command: EOL

Enter name of layer: A-Beam

Found 1 entity on layer A-Beam

Command: EOL

Enter name of layer: A-Concrete

Found 1 entity on layer A-Concrete

Command: EOL

Enter name of layer: Furniture

Found 14 entities on layer Furniture

Command: EOL

Enter name of layer: Legs

Found 16 entities on layer Legs

It would be straightforward to do something more than just count the results of the GetEntitiesOnLayer() function, of course, or even to adapt the function to filter on other properties.

2 responses to “Finding all the AutoCAD entities on a particular layer using .NET”

  1. Fernando Malard Avatar
    Fernando Malard

    Hi Kean,

    This approach works fine for small amount of entities.

    What I did for huge drawings was a solution using a database reactor and a map to store an AcDbObjectIdArray of entities for each Layer's AcDbObjectId which will be the map KEY.

    The idea was to catch these events:
    objectAppended
    objectErased
    objectModified
    objectReAppended
    objectUnAppended

    For each notification, get the entity`s layer AcDbObjectId and add/remove into proper map entry array.

    The result is a instant cache to get the entity list for each desired layer.

    You can store this cache through an AcDbObject entry inside NOD or build it on the fly everytime you load the DWG. The last will cause a small delay when loading the drawing but will avoid the use of a custom entity or a XRecord entry.

    Of course this cache must be per-document and MDI-aware.

    Regards,
    Fernando.

  2. Kean Walmsley Avatar

    That's right - this will be adequate for many (if not most) cases, but when performance is an issue implementing a cache is the way to go.

    Kean

  3. Thanks for these informative posts. I thought that with this one, I could write a function to select all entities from a list of layers, but I am ignorant of what we are doing with the "TypedValue" calls. Could you direct me to where I can get more info on this?

    Thanks,
    Allan

  4. Allan,

    You need to use the "or" operator (by adding enclosing "<or" and "or>" elements to the list):

    TypedValue[] tvs =
    new TypedValue[4] {
    new TypedValue(
    (int)DxfCode.Operator,
    "<or"
    ),
    new TypedValue(
    (int)DxfCode.LayerName,
    layerName
    ),
    new TypedValue(
    (int)DxfCode.LayerName,
    layerName2
    ),
    new TypedValue(
    (int)DxfCode.Operator,
    "or>"
    )
    };

    This is a good topic for a follow-up post.

    Kean

  5. Hi Kean,
    what the "GetAllObjects() will return the objects accessed via (or added to) the transaction " means? I found if you want to use tr.GetAllObjects(), this transaction should not be commited, in an other words if tr.commit() after tr.GetAllObjects(), when you close the AutoCAD, it collapse,why?

  6. Hi Travor,

    This is what the documentation says on GetAllObjects():

    "This function returns all the objects currently open in the transaction."

    I haven't actually used it, that I can remember, as I tend to keep track of the objects I need rather than asking the transaction for them.

    That said, if there's a problem with the way it works, it should really be reported via ADN. If you're not a member, I suggest posting your code to the AutoCAD .NET Discussion Group.

    Regards,

    Kean

  7. Hello Kean,

    i will use the SelectAll(...)
    on a programming-loaded DWG.
    But the SelelctAll throw a
    "eNotApplicable" Error.

    Why?

    Regards

    Mario

    Code:
    [code]
    public static ObjectIdCollection GetSelection(string Layername, string TypeNames, AcadDocument FromThisDWG)
    {
    Document acDocument = Document.FromAcadDocument( FromThisDWG);
    Editor acEditor = acDocument.Editor;
    if (string.IsNullOrEmpty(Layername) && string.IsNullOrEmpty(TypeNames)) return null
    if (!clLayer.HasLayer(Layername, FromThisDWG)) return null;

    TypedValue[] typedValue;
    if (string.IsNullOrEmpty(TypeNames))
    {
    typedValue = new TypedValue[1] { new TypedValue((int)DxfCode.LayerName, Layername) };
    }
    else
    {
    typedValue = new TypedValue[2] {
    new TypedValue((int)DxfCode.LayerName, Layername),
    new TypedValue((int)DxfCode.Start, TypeNames)
    };

    }
    SelectionFilter selectionFilter = new SelectionFilter(typedValue);
    PromptSelectionResult acPromptSelectionResult = acEditor.SelectAll(selectionFilter); //=== eNotApplicable-Error

    if (acPromptSelectionResult.Status == PromptStatus.OK)
    {
    return new ObjectIdCollection(acPromptSelectionResult.Value.GetObjectIds());
    }
    return null;
    }
    [/code]

  8. Kean Walmsley Avatar

    Hello Mario,

    I assume that's because Editor.SelectAll() needs to work on drawings loaded into the AutoCAD editor.

    You would need a more manual approach to "side" drawings, such as iterating through the various BlockTableRecords of interest (the modelspace, etc.).

    Regards,

    Kean

  9. Hi Kean!
    The Autocad .Net Developers Guide says concerning the Editor.SelectAll:
    "Selects all objects in the current space in which are not locked or frozen." (Source: docs.autodesk.com/ACD/2011/PTB/filesMDG/WS1a9193826455f5ff-3859b43c1209703a838778b.htm)

    That is not what (ssget "_X") respectively (ssget "X") in Autolisp does. (ssget "_X") gets really all entities from all spaces including also entities on frozen or locked layers.

    I did not try Editor.SelectAll completely. Do you know whether the .Net Dev Guide made a mistake with that information or whether it is correct?

    Regards, Stephan

  10. Ok, I didn't want to be lazy and tried it out myself:

    The Autocad .Net Developers Guide is wrong in this case!!! Editor.SelectAll acts (obviously) exactly like (ssget "_X") respectively (ssget "X"), which actually is the same but the "_X" version is needed in (older ?) localized Autocad versions.

  11. Thanks for giving it a try, Stephan.

    Please go ahead and submit this via the feedback link in the help.

    Regards,

    Kean

  12. Dear Kean,

    sadly ed.SelectAll and/or ssget "X" will return objects without objects of xrefs. Is there a way to get all the ObjectIDs of the current Database? In AutoLisp i was able to select Xref entities by nentsel.

    regards, max.

  13. Hi Max,

    (nentsel) is available in .NET as Editor.GetNestedEntity().

    I hope this helps,

    Kean

  14. Hi Kean,
    getnestedentity prompts for a entity selection. i wasnt able to convert my selectall entities into promptnestedentityresults.
    i thought about using a objectidcollection from the xrefs as the xrefs are stored as blocktablerecords. the only way to get them seems to be by explode(). but iam not quite sure if the objectids returned by explode are the same as those inside the blockreference? or are the exploded objs copies of the objects in the blocktablerecord with different objids?
    or do you know a way how to get all objectids of the current database?
    regards, max

  15. Kean Walmsley Avatar

    Hi max,

    This is evolving into a support thread (which I unfortunately don't have time to help with).

    Please submit via ADN or post your question to the AutoCAD .NET Discussion Group.

    Regards,

    Kean

  16. dear Kean,

    thanks anyway. i found a solution: as all objectids must be appended to the modelspace blocktablerecord i can just cycle through all the objectIds in the BlockTableRecord.ModelSpace.
    Then get the Entity and add the ObjectId to a ObjectIdCollection if the Layer Property matches the Layer i am looking for.
    Kind a SelectAll without depending on the Editor.

    regards,
    max

  17. Thanks for this post, really helped me.
    Max's solution helped me 2.

  18. Dear Kean, how to Highlight entities found in the layer ?
    regards, Igor Pinho.

  19. Kean Walmsley Avatar

    Dear Igor,

    You should be able to loop through the list of entities (or the selection set) and call Entity.Highlight() on each.

    Best regards,

    Kean

  20. xanhnhnn280683@gmail.com Avatar
    xanhnhnn280683@gmail.com

    Hello Kean Walmsley
    You give me an example of finding how many objects the same, thank you.

  21. As threatened, I'm now going to have to block you from commenting.

    This is not a support forum and each post you've used to ask your question is unrelated to it.

    Kean

  22. Now I have a question that bugs me everytime I think of using this .NET approach for Autocad:

    Why in the heavens Autodesk is following this line when the Interop library does this FAR MORE EASILY???

    See:

    AcadDocument Doc = Application.ActiveDocument;
    Doc.ModelSpace.OfType<acadentity>().Where(E => E.Layer == "DesiredLayerName");

    I really, really, can't believe WHY so many troubles with those transactions and filters and all that complications holding back the developer.

    It's not just a complaint, I really want to know why Interop is getting forgoten.

  23. I mean OfType{AcadEntity>()
    (Substitute the { by the "lower than" symbol)

  24. Most of the code fragment you've posted is actually LINQ, which works just as well with .NET objects as it does with COM ones, especially now that the dynamic keyword allows you to query arbitrary properties of objects from an ObjectId (effectively opening them on the fly).

    Please also bear in mind that the post you've commented on is 5 years old. For those not wanting to use "low level" .NET calls (which are as they are - however frustrating that may be - for lots of good reasons) there are number of alternatives.

    One of which is to keep using interop: type embedding even allows you to reduce your dependency on type libraries, if you choose to do so. I prefer not to, but that's my choice.

    Kean

  25. Hi Kean,

    Is it posible to have multiple condition like get all entities from layer1, layer2, layer3 whose type are polyline?

    Thanks

  26. i think i know how to solve my question.

    Dim tvs As TypedValue() = New TypedValue(1) {}
    tvs(0) = New TypedValue(0, "object type")
    tvs(1) = New TypedValue(8, "layername")

    Thanks and regards
    Sherwin

  27. thaaaaaaaaanks for this. it's a huge help when learning.

    is there a place where i can locate specific information about methods, properties and classes? I am finding that the autocad .net developer's guide - while useful - is lacking the thorough comprehensiveness of MSDN libraries.

    chrs

    1. The "one stop shop" for AutoCAD development is the AutoCAD Developer Center:

      autodesk.com/develop...

      Hopefully you'll manage to find the information you need via that. If not, let me know: I do take suggestions for new blog topics.

      Kean

  28. It's amazing how often I have a fairly specific question that you've already answered--usually years before, like this post. Thanks, Kean!

  29. Hello... nice post... Thanks...

    But I have a question...

    How to get all properties from the entities...

    I would like to see all entities and yours properties in a list...

    Thanks...

    1. Kean Walmsley Avatar

      That shouldn't be hard to do. But I'm currently unavailable (and anyway don't have time to do custom coding for people). Please post to the AutoCAD .NET forum...

      Kean

  30. Hello Kean,
    Thank You for your efforts
    My question is how I can get a specific property of object.

    I tried to loop through ObjectIdcollection but I didn't get any method that access to a specific property of object inside the same layer.

    Thanks in advance !

    1. Hi Ali,

      Please ask your support questions on the AutoCAD .NET forum.

      Many thanks,

      Kean

  31. Hello Kean,

    Thanks for your sharing.

    A strange problem happened to me through this method. The layer will not be select if the layer's name contains character "#".

    If the layer name is "#", it will select the default "0" layer.

    Many thanks to you for reviewing and considering this question~

    Best~
    ZH

  32. Hello Kean,

    Thanks for your sharing,

    I have encountered a problem by using this method. I have posted a discussion on the AutoCAD .NET forum. It is a problem that the selection fails if the layer name contains the special character "#", which is probably related to Regular Expression.

    forums.autodesk.com...

    Many thanks to all who can refer to it,

    Best~

    ZH

Leave a Reply to Stephan Bartl Cancel reply

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