How would I add or remove from a selection set that is currently active.

How would I add or remove from a selection set that is currently active.

nshupeFMPE3
Advocate Advocate
1,093 Views
8 Replies
Message 1 of 9

How would I add or remove from a selection set that is currently active.

nshupeFMPE3
Advocate
Advocate

I would like to make a command that has two "modes".

An "Adding" mode, when entities are selected other entities that share the same layers as the entities that were selected are added to the selection set.
And a "removing" mode, when entities are selected in this mode any entities on the same layers as the selected entities are removed from the selection set. 

I tried to use event handlers for Editor.SelectionAdded and Editor.SelectionRemoved. But that became a nightmare of infinite event triggers. But even after solving that I couldn't figure out how to add to the selection set. I tried Editor.SetImpliedSelection but that never changed the selection set. 

I've also tried using PromptSelectionOptions.KeywordInput event handlers. But that will only let you Add to the selection, not remove from it. 

Any help would be greatly appreciated.

0 Likes
Accepted solutions (2)
1,094 Views
8 Replies
Replies (8)
Message 2 of 9

ActivistInvestor
Mentor
Mentor

The SelectionAdded event handler receives a SelectionAddedEventArgs instance that has both Add() and Remove() methods that are used to add/remove objects from the selection.

 

 

0 Likes
Message 3 of 9

nshupeFMPE3
Advocate
Advocate

Thank you for the reply. I gave the Add() method a try and encountered an assertion error? I tried to look in the documentation for Add() and SelectedObject.Ctor() but didn't get much help from either. 

nshupeFMPE3_0-1735920491357.png

private static void EdOnSelectionAdded(object sender, SelectionAddedEventArgs e)
{
    if(_db is null) return;
    Transaction tr = _db.TransactionManager.StartOpenCloseTransaction();

    var layers = e.AddedObjects.GetObjectIds()
        .Select(id => tr.GetObject(id, OpenMode.ForRead) as Entity)
        .Select(ent => ent.Layer)
        .ToList();

    var otherObjects = _allEntities
        .Where(ent => layers.Contains(ent.Layer))
        .Select(ent => ent.ObjectId)
        .Where(id => !e.AddedObjects.GetObjectIds().Contains(id))
        .ToList();

    foreach (var id in otherObjects)
    {
        e.Add(new SelectedObject(id, new SelectedSubObject[1]));
    }

    tr.Commit();
}


I think this code is fairly simple. This is the event handler for Editor.SelectionAdded. Its in a static class with a method that is started through a command. That method asks the user to make a selection of items for which to search through. Then attaches the event handler and prompts the user for another selection. Once a selection is made, the above event triggers. I find other entities that are within the original selection, and then attempt to add them to the selection withing the foreach. but each time Add() is called the above assertion is triggered. 

0 Likes
Message 4 of 9

ActivistInvestor
Mentor
Mentor

.

0 Likes
Message 5 of 9

ActivistInvestor
Mentor
Mentor

The exception is pretty-much telling you what the problem is. 

 

You can't specify a graphical selection method for objects added to the selection, and I'm not sure why you're using the wrong constructor for SelectedObject, but this should work:

 

e.Add(new SelectedObject(id, SelectionMethod.NonGraphical, IntPtr.Zero));

 

Going beyond the above issue, your code has significant performance issues. The telltale sign of that is the use of List<T>.Contains(), which is an (On) linear search verses an (O1) search done by a hashset. In cases where the objects in the List are functionally a set (e.g., no duplicates), you should always use a HashSet<T> when there are many lookups needed.

 

With that, this is how I would have written it:

 

static class Class1
{
   /// <summary>
   /// If this is a field of a static class, what is the
   /// scope of the entities in the list ? 
   /// </summary>
   static List<Entity> _allEntities = new List<Entity>();
   
   static void EdOnSelectionAdded(object sender, SelectionAddedEventArgs e)
   {
      using(Transaction tr = new OpenCloseTransaction())
      {
         var layerIds = new HashSet<ObjectId>(
            e.AddedObjects.GetObjectIds()
               .Select(id => (tr.GetObject(id, OpenMode.ForRead) as Entity).LayerId));

         var addedIds = new HashSet<ObjectId>(e.AddedObjects.GetObjectIds());

         var otherObjects = _allEntities
            .Where(ent => layerIds.Contains(ent.LayerId)
               && !addedIds.Contains(ent.ObjectId))
            .Select(ent => ent.ObjectId);

         foreach(var id in otherObjects)
         {
            e.Add(new SelectedObject(id, SelectionMethod.NonGraphical, IntPtr.Zero));
         }

         tr.Commit();
      }
   }
}

 

 

 

 

  

0 Likes
Message 6 of 9

nshupeFMPE3
Advocate
Advocate

Thank you for the solution and the code example. With so little to go by with the Docs I wasn't clear if I should just keep trying other constructors, especially those I'm unfamiliar with and take IntPtr's which I'm really not familiar with. The suggestion for HashSet is also very good. I've been trying to get this to simply work so I defaulted to Lists, but once I get a working prototype HashSets will be a definite place to improve. 

the above suggestion did work thankfully so I am able to add and remove to the selection set. One follow up question I have is if it is possible to "pre-populate" a selection set. I'd like to be able to do a Editor.GetSelection with some blocks already in the selection set. That way they will be highlighted and will allow for the normal operation of Shift+Click or entering "Remove" Keyword to remove blocks from the selection set. 

Unfortunately if I use Editor.SetImpliedSelection(preSelectedBlockIds) and follow with a Editor.GetSelection() with or without an options argument, the selection automatically ends with no user input. Is there a way to override this behavior? I tried an Event handler for Editor.PromptedForSelection event but the Args have no way to manipulate the selection set.

0 Likes
Message 7 of 9

ActivistInvestor
Mentor
Mentor
Accepted solution

@nshupeFMPE3 wrote:

 

One follow up question I have is if it is possible to "pre-populate" a selection set. I'd like to be able to do a Editor.GetSelection with some blocks already in the selection set. That way they will be highlighted and will allow for the normal operation of Shift+Click or entering "Remove" Keyword to remove blocks from the selection set. 

Unfortunately if I use Editor.SetImpliedSelection(preSelectedBlockIds) and follow with a Editor.GetSelection() with or without an options argument, the selection automatically ends with no user input. Is there a way to override this behavior? I tried an Event handler for Editor.PromptedForSelection event but the Args have no way to manipulate the selection set.


 

The code I posted here a little over 12 years ago that shows how to do what you're seeking to do was deleted by Autodesk last month. If you find that disappointing, feel free to communicate your views to the product manager @Tiana_Y . 

 

So, as a result of Autodesk deleting that post, I had to waste my time to find and make it available again, which will most-definitely not be here. See this file, containing that code that was originally posted here ~12 years ago, which BTW, still works in AutoCAD 2025.

 

And BTW, regarding Autodesk deleting old-but-relevant content, what AutoCAD releases are you using/targeting with this code?

Message 8 of 9

nshupeFMPE3
Advocate
Advocate

The deletion of older posts is very (extremely) disappointing @Tiana_Y. I use this forum (as ActivistInvestor and my record can attest to) almost everyday to learn how to use the .NET API for AutoCAD to its fullest extent. The official documentation is fairly good, but very far from a complete source of information on how to effectively utilize the API in more than a basic fashion. 

@ActivistInvestor to answer your question, I am currently rewriting my plugin (more of a house cleaning measure than a reflection of the API btw) to target .NET 8 for AutoCAD 2025. But I still maintain the 2024 and earlier version of AutoCAD with my plugin for the time being. 

  On the topic of your shared code, I think this will work great. Thank you once again. If I may ask, about this part of the code where it seems the "magic happens"

public PromptSelectionResult EditSelection()
{
    if (this.selection.Any())
    {
        ed.Document.SendStringToExecute("Edit\n", true, false, false);
    }
    return ed.GetSelection(options);
}

 Is this working in a way where you send the string to execute, and then start the selection? I would have thought the selection would have needed to be started before sending the "Edit" keyword. Just curious on the order of operations/timing that allows this to work.

Hopefully the answer will be here for years to come, so others might learn (looking at your Autodesk).

0 Likes
Message 9 of 9

ActivistInvestor
Mentor
Mentor
Accepted solution

If you do a search here on "SendStringToExecute", you'll find dozens or perhaps even hundreds of cases where people used it believing that it executes its commands argument string at the point when it's called. It doesn't do that. SendStringToExecute() is 'asynchronous', meaning that it waits until AutoCAD reaches the point where it begins waiting for the user to enter input on the command line, which in this case, is just after GetSelection() is called and the "Select objects: " prompt appears, so that's when it gets the keyword, triggering the keywordInput() event handler to run, which adds the initial objects to the selection.

 

When I finally located that code in my archives, I also found another more-recent version of it that addresses a few minor issues involving the display of a confusing message that appears after it gets the keyword, and also adds the ability to zoom the display to the extents of the existing selection set that's being edited, which you can find here.

 


@nshupeFMPE3 wrote:

The deletion of older posts is very (extremely) disappointing @Tiana_Y. I use this forum (as ActivistInvestor and my record can attest to) almost everyday to learn how to use the .NET API for AutoCAD to its fullest extent. The official documentation is fairly good, but very far from a complete source of information on how to effectively utilize the API in more than a basic fashion. 

@ActivistInvestor to answer your question, I am currently rewriting my plugin (more of a house cleaning measure than a reflection of the API btw) to target .NET 8 for AutoCAD 2025. But I still maintain the 2024 and earlier version of AutoCAD with my plugin for the time being. 


So here we have another of what I anticipate will be hundreds of cases of a customer using and targeting current releases of AutoCAD, knowingly being denied the benefit of existing peer support, that effectively demonstrates how the purging of this resource undertaken last month, injures not only customers using older product releases, but also those paying for and using current versions of AutoCAD and its toolsets as well.

 

The only word that comes to mind is incomprehensible.