Preventing AutoCAD objects from being selected using .NET

In the last post, we looked at how to stop entities from being highlighted during selection. This post looks at how to stop entities from being selected at all. Thanks again to Balaji Ramamoorthy for providing the underlying technique shown in today's code.

The basic scenario we're using is similar to the last post โ€“ we maintain a list of DXF names for the classes we want to stop from being selected โ€“ but it could easily be adapted to using different criteria for removing objects from the selection: an example being the use of a similar (although admittedly not identical, as we respond to a different event) technique in the OffsetInXref Plugin of the Month to remove a selected xref from the current selection and replace it with a cloned copy of the nested object.

If we were using single entity selection (GetEntity() rather than GetSelection()) then we'd probably use a different approach when filtering at a class level: we'd probably go with PromptEntityOptions.AddAllowedClass() to enable specific classes. But we could also use this technique with entity picking, in case, especially if we used other criteria for disallowing selection (i.e. not based on the object's type) or wanted to disallow certain types rather than allowing others.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System.Collections.Generic;

using System.Text;

 

namespace PreventSelection

{

  public class Test : IExtensionApplication

  {

    void OnSelectionAdded(object sender, SelectionAddedEventArgs e)

    {

      ObjectId[] addedIds = e.AddedObjects.GetObjectIds();

      for (int i=0; i < addedIds.Length; i++)

      {

        ObjectId oid = addedIds[i];

 

        if (IsInList(oid.ObjectClass.DxfName))

        {

          e.Remove(i);

        }

      }

    }

 

    [CommandMethod("US")]

    public static void Unselect()

    {

      Document doc = Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      // Print the list of currently unhighlighted classes

 

      ed.WriteMessage(ListToPrint());

 

      // Get the type to add to the list

 

      PromptResult pr =

        ed.GetString(

     
0;   
"\nEnter the type of object to stop from " +

          "being selected: "

        );

      if (pr.Status != PromptStatus.OK)

        return;

 

      if (IsInList(pr.StringResult))

      {

        ed.WriteMessage("\nItem already in the list.");

      }

      else

      {

        AddToList(pr.StringResult);

        ed.WriteMessage("\nItem added to the list.");

      }

    }

 

    // Would call this command RS, but it's taken by RSCRIPT,

    // so using the somewhat unwieldy UUS, instead

 

    [CommandMethod("UUS")]

    public static void Ununselect()

    {

      Document doc = Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      // Print the list of currently unhighlighted classes

 

      ed.WriteMessage(ListToPrint());

 

      // Get the type to remove from the list

 

      PromptResult pr =

        ed.GetString(

          "\nEnter the type of object to remove from the " +

          "list: "

        );

      if (pr.Status != PromptStatus.OK)

        return;

 

      if (!IsInList(pr.StringResult))

      {

        ed.WriteMessage("\nItem not currently in the list.");

      }

      else

      {

        RemoveFromList(pr.StringResult);

        ed.WriteMessage("\nItem removed from the list.");

      }

    }

 

    void IExtensionApplication.Initialize()

    {

      Document doc = Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      ed.SelectionAdded +=

        new SelectionAddedEventHandler(OnSelectionAdded);

    }

 

    void IExtensionApplication.Terminate()

    {

      Document doc = Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

 

      ed.SelectionAdded -=

        new SelectionAddedEventHandler(OnSelectionAdded);

    }

 

    // The list of types to unhighlight

 

    static List<string> _unhighlighted = new List<string>();

 

    // Add a type to the list

 

    public static void AddToList(string name)

    {

      string upper = name.ToUpper();

      if (!_unhighlighted.Contains(upper))

      {

        _unhighlighted.Add(upper);

      }

    }

 

    // Remove a type from the list

 

    public static void RemoveFromList(string name)

    {

      string upper = name.ToUpper();

      if (_unhighlighted.Contains(upper))

      {

        _unhighlighted.Remove(upper);

      }

    }

 

    // Check whether the list contains a type

 

    public static bool IsInList(string name)

    {

      return _unhighlighted.Contains(name.ToUpper());

    }

 

    // Get a string printing the contents of the list

 

    public static string ListToPrint()

    {

      string toPrint;

 

      if (_unhighlighted.Count == 0)

      {

        toPrint =

          "\nThere are currently no objects in the list " +

          "to stop from being selected.";

      }

      else

      {

        StringBuilder sb =

          new StringBuilder(

            "\nObjects of these types will not be selected:"

          );

        foreach (string name in _unhighlighted)

        {

          sb.Append(" " + name);

        }

        toPrint = sb.ToString();

      }

 

      return toPrint;

    }

  }

}

Once again, we'll take a look at the code running on some basic geometry:

Our simple geometry for selection

And we can see it's all very selectable:

All our selected geometry

Then we can use our US command to force circles and lines from not being selected:

Command: US

There are currently no objects in the list to stop from being selected.

Enter the type of object to stop from being selected: circle

Item added to the list.

Command: US

Objects of these types will not be selected: CIRCLE

Enter the type of object to stop from being selected: line

Item added to the list.

Which we can see means our arcs are the only objects selected when we window-select everything:

Our partially selected geometry

14 responses to “Preventing AutoCAD objects from being selected using .NET”

  1. Kean,

    This is slightly off topic, but what I'm working on today is pretty similar to what you're doing as far as reacting to selection set changes (ed.SelectionAdded).

    How can I react to entities being removed from the pickfirst set in C#? I tried ed.SelectionRemoved but it doesn't seem to get triggered, ever. When I deselect all entities SelectionAdded gets triggered again, but e has the same values that it did when the entities were added. ARX has a pickfirstModified overload on the AcEditorReactor, but I can't find a C# wrapper.

    A post about reacting to entities getting deselected could be a nice sequel to today's post. Maybe do something silly like draw a big red X across any entities that get deselected (using the corners of the bounding box)?

  2. Chuck,

    Actually this is pretty nicely on-topic, compared to many of the comments I get. ๐Ÿ˜‰

    You might start with Document.ImpliedSelectionChanged() (yes, it took me some time to find it, even though I know it was there).

    This only gets a System.EventArgs parameter, so you probably need to do some with Editor.SelectImplied() and Editor.SetImpliedSelection() if you need to query or modify the contents of the pickfirst set (and I don't know if that's possible from the callback itself - you may need to defer such changes until DocumentManager.DocumentLockModeChanged()).

    Seems worthy of a post - will try to get to it in the next week or two.

    Cheers,

    Kean

  3. Having read your blog (and the comments) for a while, I didn't feel TOO bad about the off-topicness of my question ๐Ÿ˜‰

    ImpliedSelectionChanged is exactly what I was looking for, for my purposes (updating a modeless property display palette as the selected entities change, so I only care about the current selection). I'm guessing that's the wrapper for ARX's pickfirstModified.

    Getting the entities that were just de-selected by the change to the implied selection seems doable, though kind of hokey given just this event to work with. I guess you'd have to store the last known selection set as a member, then compare to the current selection in the event handler. Seems weird to have to do all that when the Editor's SelectionRemoved event is sitting right there...

  4. I agree - very hokey. I just gave it a try and ended up doing exactly what you'd mentioned (although I held an ObjectIdCollection rather than a full selection set).

    Editor.SelectionRemoved() would have been great, but yes - that's wired differently (and under-the-hood work would presumably be needed to map pickfirst management into it).

    I have it working, with minor quirks: the code can't easily tell the difference between someone deselecting everything (via a crossing window, for instance) and hitting escape (which we really have to allow to work in the same way). So objects will get deselected if *everything* is deselected. :-S

    Kean

  5. wow this is close to what I'm trying to accomplish...(chuck's comments)

    I too need to get a mode-less palette to adapt to the current implied selection. in my case it seems the event [SelectionAdded] fires so often I can't even sort it, even when you only pick 1 entity, once. Did you guys ever look deeper into this?

  6. I don't seem to have found anything beyond what was posted in the comments.

    That said, I suggest looking at the ImpliedSelectionChanged event - this post should be a good start:

    keanw.com/2011/10/displaying-an-autocad-ribbon-tab-contextually-using-net.html

    Kean

  7. I may be double posting here - I found that article and tried to put it here THEN saw the post Kean - your a legend ๐Ÿ™‚ yes, that worked perfectly for palettes. The c# to VB.net conversion went pretty smooth but... that AddHandler to isIdle worries me; where / how could you remove the handle? I'm trying to be really careful about unloading (eventually) pretty much ever handler I load.... still, working is working.

  8. As far as I recall there is a RemoveHandler() method you can use from VB.NET. You can remove it on Terminate(), but that's really only called on AutoCAD shutdown (you might also choose to remove it sooner, as you see fit).

    Kean

  9. can you help me handle event selectionRemoved? I think it not working.

  10. This is not a forum for support. Please post your questions to the AutoCAD .NET Discussion Group.

    Kean

  11. Hi Kean,
    Great blog! Still trying to feel my way around the API and am playing around with dynamic blocks, so I've found this article very useful. But I'm having problems with ed.SelectionAdded() and doc.ImpliedSelectionChanged() handlers - they do not give me the blockreference that I select - I just get PickList or PolyLine. How do I get the selected blocks (eg added to a List<>) as they are selected?

    1. Hi Paul,

      Thanks!

      Editor.SelectionAdded works just fine for me with block references: I just added a handler that reports back the handle and object class of the selected object, and the object selected for blocks is indeed the reference, not the contents.

      Is the selection method you're using doing a nested selection, by any chance?

      I suggest posting your code to the AutoCAD .NET Discussion Group (unless the problem can be shown using the code in the post, of course).

      Regards,

      Kean

      1. Hi Kean,
        Thanks for your reply, which has stumped me a bit. What I want to achieve is to be able to perform operations on a dynamic block that affect 'contained' dynamic blocks (move stretch etc).
        Incidentally your EntityFramework looks like it has good potential but I haven't worked out yet how to use enitityjig without prompting for selections, so I'm coding it long hand for now.

Leave a Reply to ko0ls Cancel reply

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