Conditional selection of AutoCAD entities using .NET

This post was inspired by a comment on this previous post, where we looked at some code to select entities on a specific layer. The question was regarding how best to select entities from multiple layers: the selection filtering mechanism inside AutoCAD makes this very easy, and can cope with composition of conditions related to various entity properties.

The basic concept is to enclose sets of entity properties for which you wish to filter with tags indicating the composition of the conditions: for "or" you enclose the conditions with "<or" and "or>" and for "and" you use "<and" and "and>". Wow: I can safely say that that's probably the only sentence I've ever written that has 6 of the last 10 words being "and". 🙂

Let's take a concrete example: let's say we want to select all lines on on layer 0 and all the circles with radii greater than 10.'s how we would compose the conditions, in pseudo-code:

  • <or
    • <and
      • Layer == "0"
      • Entity type == "LINE"
    • and>
    • <and
      • Entity type == "CIRCLE"
      • Radius >= 10.0
    • and>
  • or>

This translates into the following C# code - for clarity I've left the specific properties/values hard-coded, but clearly it would be straightforward to ask the user or pick them out of a database, as needed.

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

namespace EntitySelection

{

  public class Commands

  {

    [CommandMethod("SEWP")]

    static public void SelectEntitiesWithProperties()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      // Build a conditional filter list so that only

      // entities with the specified properties are

      // selected

      TypedValue[] tvs =

        new TypedValue[] {

          new TypedValue(

            (int)DxfCode.Operator,

            "<or"

          ),

          new TypedValue(

            (int)DxfCode.Operator,

            "<and"

          ),

          new TypedValue(

            (int)DxfCode.LayerName,

            "0"

          ),

          new TypedValue(

            (int)DxfCode.Start,

            "LINE"

          ),

          new TypedValue(

            (int)DxfCode.Operator,

            "and>"

          ),

          new TypedValue(

            (int)DxfCode.Operator,

            "<and"

          ),

          new TypedValue(

            (int)DxfCode.Start,

            "CIRCLE"

          ),

          new TypedValue(

            (int)DxfCode.Operator,

            ">="

          ),

          new TypedValue(

            (int)DxfCode.Real, // Circle Radius

            10.0

          ),

          new TypedValue(

            (int)DxfCode.Operator,

            "and>"

          ),

          new TypedValue(

            (int)DxfCode.Operator,

            "or>"

          )

        };

      SelectionFilter sf =

        new SelectionFilter(tvs);

      PromptSelectionResult psr =

        ed.SelectAll(sf);

      ed.WriteMessage(

        "\nFound {0} entit{1}.",

        psr.Value.Count,

        (psr.Value.Count == 1 ? "y" : "ies")

      );

    }

  }

}

By the way - you can also choose to perform an "exclusive or" test by using "<xor" and "xor>".

To try out this code, draw a number of lines in a blank drawing, and run the SEWP command. This simply tells you how many entities met the selection criteria - it doesn't leave them selected for use by further commands. You can then see how drawing circles of varying radii changes the number of entities selected by the command.

On a final note... SelectionFilters can be used either non-interactively (as in this example, via the SelectAll() method) or interactively (via GetSelection(), SelectWindow(), SelectCrossingPolygon(), SelectFence(), etc.). I've shown simple uses of SelectionFilters in previous posts, but it's also possible to use quite complicated groupings of conditions - as we've scratched the surface of in this post.

2 responses to “Conditional selection of AutoCAD entities using .NET”

  1. Thanks Kean,

    This question has a very thin relatationship with the post, but still posting it in a hope that possibly this could even turn out to be a post one day!

    Okay, the question is that what should I do if I want to expose properties of my custom entity to AutoCAD's filter mechanism. For example, if I have a custom entity called myCircle, derived from AcDbEntity has a property mRadius. I want the users to use AutoCAD standard mechanism to filter all myCircle entities with values of mRadius is greater than 20.

    Thanks,
    Narayanan

  2. Kean Walmsley Avatar

    Hi Narayanan,

    Actually this is reasonably closely related, but unfortunately I don't know of a way to customize the FILTER dialog: there would need to be a way to connect descriptions of properties with their underlying DXF group-code (and the datatype they contain).

    That said, the FILTER command only really uses the internal mechanism described in this post, so you could implement your own tailored feature that works with your objects.

    I hope this helps,

    Kean

  3. The "DxfCode.Operator" dose not exist in AutoCAD2006 .Net API. Any good advise?

  4. I don't know whether this means the API wasn't present then, which is quite possible.

    You could try replacing DxfCode.Operator with -4 (its value), in case that helps.

    Kean

  5. Hi Kean,
    How to prevent selecting objects of a layer

  6. Kean Walmsley Avatar

    Try using the "<not" / "not>" operator.

    Kean

  7. Kean,

    Is there a list of operators somewhere? I would like to select certain blocks based on block name but I'd like to do something where it compares the beginning of the block name (ie: blockName.StartsWith("...")) instead of looking for a specific match.

    Thanks,
    Mark

  8. Kean,

    Is there a list of operators somewhere? I would like to select certain blocks based on block name but I'd like to do something where it compares the beginning of the block name (ie: blockName.StartsWith("...")) instead of looking for a specific match.

    Thanks,
    Mark

  9. Mark,

    The information I have is in the ObjectARX Reference, at the bottom of the page on acedSsGet():

    >>>
    Conditional Filtering

    The relational operators are binary operators. You can also test groups by creating nested Boolean expressions that use the conditional operators shown in the following table. The conditional operators are specified by -4 groups, like the relational operators. They are paired and must be balanced correctly in the filter list or the acedSSGet() call fails. The number of operands that these operators can enclose depends on the operation, as shown in the table.

    Conditional operators for selection set filter lists:

    Starting operator  Encloses               Ending operator  
    "<AND" One or more operands "AND>"
    "<OR" One or more operands "OR>"
    "<XOR" Two operands "XOR>"
    "<NOT" One operand "NOT>"

    With the conditional operators, an operand is an entity field group, a relational followed by an entity field group, or a nested expression created by these operators.

    Conditional expressions that test for extended entity data (using the -3 group) can contain only -3 groups.

    The conditional operators are not case sensitive; you can also use their lowercase equivalents including , and>, , or>, , xor>, , and not>.
    <<<

    It sounds as though you want to do a wildcard comparison (I'd start by trying "...*" as the comparison string).

    Regards,

    Kean

  10. Hi Kean!

    Do I have the possibility to add a message to be displayed for the SelectionFilter? I've looked in the Autocad Developer, but all the examples don't show if this feature exist or not.

  11. You should put an edit button. 😀

    I have another question: Could I set the filter to allow just one entity(which meets the conditions) to be selected?

  12. Kean Walmsley Avatar

    Hi Peter,

    If you use the version of Editor.GetSelection() which takes a PromptSelectionOptions and a SelectionFilter, you should have control over these things.

    Regards,

    Kean

  13. Thanks Kean, I didn't saw the overload method. 😀

  14. Kean,

    I have a list of Civil3D surfaces in which I can select through basic auto cad quick properties via "Tin Surface" object type and "Name" property. Is there someway I can use the dfxcode operators to do this automatically? I have been searching for a list of what all of these operators do/mean, maybe that would be a good post too!!

    Thanks in advance. Your blog is great by the way.

    Steve
    Civil3D 2011

  15. Steve,

    Assuming Civil 3D objects support the DXF protocol, you should be able to select them using these operators. But I don't know whether that's the case, as I don't use Civil 3D - you may want to check in with the ADN team or perhaps Isaac via his blog.

    Regards,

    Kean

  16. Thanks. I will follow up with him.

    Steve

  17. Just to follow up with anyone intersted, I found some good information on all the DXFCODEs available and a description of what they do. I put the links on my blog as well as an updated table related to the namespace. It can be found here.

  18. Hey Kean!

    I've got a question. I need to select blocks with specified names (i.e. myBlock_1, myBlock_2).

    I wrote the code below:
    TypedValue[] filList = {
    new TypedValue((int)DxfCode.Operator,"<or"), new="" typedvalue((int)dxfcode.operator,"<and"),="" new="" typedvalue((int)dxfcode.start,="" "insert"),="" new="" typedvalue((int)dxfcode.blockname,="" "myblock_1"),="" new="" typedvalue((int)dxfcode.operator,"and="">"),

    new TypedValue((int)DxfCode.Operator,"<and"), new="" typedvalue((int)dxfcode.start,="" "insert"),="" new="" typedvalue((int)dxfcode.blockname,="" "myblock_2"),="" new="" typedvalue((int)dxfcode.operator,"and="">"),

    new TypedValue((int)DxfCode.Operator,"<and"), new="" typedvalue((int)dxfcode.start,="" "insert"),="" new="" typedvalue((int)dxfcode.blockname,="" "myblock_3"),="" new="" typedvalue((int)dxfcode.operator,"and="">"),
    new TypedValue((int)DxfCode.Operator,"or>"),
    };

    Code seems to work OK, but my question is there any short way to set the SelectionFilter? I was trying to do something like this:
    <or ---<and="" ---="" block.start-insert="" ---="" --="" <and="" ---="" --="" --="" <or="" block.name="">
    --- -- -- <or block.name="">
    --- -- and>
    --- and>
    --- or>

    But this doesn't work.

    Thanks.

  19. Kean Walmsley Avatar

    Hi Gregory,

    Sorry - I don't have time to debug your code. Please post it via ADN or to the AutoCAD .NET Discussion Group.

    Regards,

    Kean

  20. Hi Kean, two questions remain:
    1) How do I filter entities with certain XData-IDs. There is a "DxfCode.XDataStart" code but
    new TypedValue((int)DxfCode.XDataStart, "MY_XDATA") doesn't work. The AutoCAD's "FILTER" command (that provides the serialization of custom filters to a filter.nfl file in the appdata directory) created the entry "( -3('MY_XDATA'))" for the filter. The syntax slightly differs to other filters that work with TypedValues, e.g. "(8 . 'MY_LAYER')".
    2) Do these filters also apply to a given (preselected) ObjectIDCollection and if yes, how?
    Thanks!

  21. Hi Matthiaa,

    I unfortunately haven't done this, myself (and don't have time to look into it, right now).

    Would you mind posting your question via ADN or the AutoCAD .NET Discussion Group?

    Thanks & regards,

    Kean

  22. Hi, thanks for answering

    to 1)
    You can achieve the XData-ID filtering by combining two TypedValues:
    TypedValueList tvl = new TypedValueList();
    tvl.Add(new TypedValue((short)DxfCode.XDataStart));
    tvl.Add(new TypedValue((short)DxfCode.ExtendedDataRegAppName, "XDATA_KEY"));

    to 2)
    As suggested I recently opened a discussion:
    forums.autodesk.com/t5/NET/How-to-apply-a-selection-filter-on-a-ObjectIDCollection/td-p/3733194

  23. Great - glad you're halfway there. 🙂

    For the second part, you might try using a set of checks for DxfCode.Handle of the various objects (ORed together, of course).

    Kean

  24. Matthias Mueller Avatar

    to 2) I moved from TypedValues to Predicates with Linq expressions since it's more flexible and only has a little performance loss.
    It also allows me e.g. to filter for entities with certain XData.

    For details: forums.autodesk.com/t5/NET/How-to-apply-a-selection-filter-on-a-ObjectIDCollection/td-p/3733194

    Matthias

  25. Is it possible to do this Conditional Selection with the DXF Codes with just opening the Database (i.e. Dont open the actual file in AutoCAD)?

  26. You need the editor for conditional selection to work: otherwise you'll have to iterate through the database to gather the objects you're interested in.

    Kean

  27. Excellent as always Kean!

    I have added a link to the AutoCad help site which discusses this exact issue and elaborates on the types of operators that can be used and provides more examples:

    help.autodesk.com/vi...

    readers may find it useful.

    rgds
    Ben

  28. Hi Kean,
    a further question which this article leads onto: what happens if one wants a selectionfilter with very very specific requirements for the item being selected?

    e.g. what if one wanted (as a selection filter) only lines whose angles are 45 degrees? or needs the selection filter to select: Green or blue lines that are between 10-7 mm thick.

    How does one be more specific about the properties of the items we want the selection filter to be captured.

    myself (and other reads i'm sure) would be very keen to know.

    rgds
    Ben

    1. Hi Ben,

      Thanks for sharing that link.

      SelectionFilters can only go so far, of course.

      If you want to get *really* specific then you're probably going to want to implement a custom mechanism, such as where you select entities - one by one - and perform some analysis before going further. Or you post process a selection set and pull out the entities that meet your criteria.

      Regards,

      Kean

  29. Hi Kean,

    is it possible to filter a selection based on a XRecord (when the XRecord is just a string)?

    Thanks,
    Sebastian.

    1. Kean Walmsley Avatar

      Hi Sebastien,

      Do you mean you want to filter for entities that have an Xrecord tagging them?

      If so, you may be able to perform the filter yourself via Editor callbacks... seems like an interesting blog topic. 🙂

      Kean

      1. Hi Kean,

        thanks for your fast answer.

        what I want to achieve is a selection of entities depending on which XRecord value it has attached on its Extension Dictionary.

        In my case we generate dwg files using information stored in an oracle database. So we decided to attach this (oracle) database information (like Database IDs but also other data) to as XRecords to the entities we generated. We just put string values. Thats what I meant in my previous comment.

        So our generated dwg may contain over 25000 entities. I don't know if that is a lot? But when I try to get entities lets say with a specific (oracle) database ID e.g. 100 I have to select all entities first, get the Extension Dictionary, check its values if it contains the database ID 100 and do a lot of casts... If I do it that way it takes over 3 seconds just to filter those entities.

        Then I found this blog and asked myself if its possible to filter my entities by xrecord using SelectionFilters.

        Sebastian.

        1. Kean Walmsley Avatar

          Hi Sebastien,

          I see. SelectionFilters won't help, here: they don't allow you to filter over Xrecord contents.

          It may be that using the SelectionAdded event will allow you to implement your own filter for interactive selection but I doubt very much that iterating through the database can be made much more efficient without creating and managing your own index.

          Regards,

          Kean

  30. Hi Kean,

    I'm quite new to this, and have been looking everywhere, but can't seem to find the answer I need. Maybe I just don't know what keywords to search. In your post, 2nd to last paragraph, you mention "This simply tells you how many entities met the selection criteria - it doesn't leave them selected for use by further commands".

    How would one leave them selected?

    Any help is greatly appreciated. Also, thank you for posting so much helpful info on this blog. Its a truly great resource!

    1. Hi David,

      You would need to add them to the "pickfirst" selection set:

      keanw.com/2007/01/adding_to_the_a.html

      I hope this helps,

      Kean

  31. Hello Kean,

    I am very much new to AutoCAD Customization but your intellectual posts which includes almost every iterations possible is really helping. Recently I got myself stuck in one of my project, hope you can help me.
    I wanted to select any 1 entity or a block (out of many) and that should trigger me to another command which has a different functioning. Example, out of an assembly drawing, selecting any one part should open the part drawing (with top ,side and front view) of the selected entity. How to specify the names and conditional statement. Thanks in advance.

    Suraj

    1. Hi Suraj,

      I'm glad to hear the blog is helpful to you.

      Unfortunately I can't help with this kind of question (in general I don't have time to provide support, and specifically right now I'm on an extended trip with my family). Please post to the AutoCAD .NET forum, instead.

      Best,

      Kean

  32. Hi Mr. Kean

    Your efforts are highly appreciated dear.

    How can I count polylines in a specific layer and print each
    ones area to a file or command line, Using managed VB.net API.

    Many thanks
    "mjd.m.ali.9@gmail.com"

    1. Hi,

      Sorry - I don't do custom development work (and right now I'm tied up elsewhere).

      Please post your request to the AutoCAD .NET forum.

      Regards,

      Kean

  33. Hi, coming back to this old post, is there a more modern way to select elements via various filter settings?
    I recently had to notice that this way using (int)DxfCode.LayerName is unable to select any elements on layers that include a hash #

    e.g. (int)DxfCode.LayerName, "test#" fails to select any elements on the Layer "test#"
    i had to end up replacing all occurances of # with * in my selection method

    1. Kean Walmsley Avatar

      Please post your support requests via the AutoCAD .NET Forum.

      Kean

  34. Hello, is this possible to select entities having a specific dictionary or a dictionary value ?

    1. Hi Sergey,

      Please post your technical questions to the AutoCAD .NET forum.

      Thanks,

      Kean

Leave a Reply to Peter Cancel reply

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