Adding an ObjectOverrule to stop AutoCAD objects from being erased using .NET

As a follow-on from the last post, today we're going to see how to actually stop the erase operation from happening for a certain type of object (in this case we're going to focus on Lines). Thanks for Stephen Preston for showing us the way in his comment on that post: inspired by his suggestion I ended up coding this before breakfast (although I do recommend taking care when throwing exceptions on an empty stomach ;-).

Here's the C# code implementing the PER (Prevent Erasure) command:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

 

namespace WeLikeToBlock

{

  public class Commands

  {

    public class EraseOverrule : ObjectOverrule

    {

      public override void Erase(DBObject dbObject, bool erasing)

      {

        base.Erase(dbObject, erasing);

 

        throw new

          Autodesk.AutoCAD.Runtime.Exception(

            ErrorStatus.NotApplicable

          );

      }

    }

 

    static EraseOverrule _theOverrule = null;

 

    [CommandMethod("PER")]

    static public void PreventErase()

    {

      Editor ed =

        Application.DocumentManager.MdiActiveDocument.Editor;

 

      if (_theOverrule == null)

      {

        _theOverrule = new EraseOverrule();

 

        ObjectOverrule.AddOverrule(

          RXObject.GetClass(typeof(Line)),

          _theOverrule,

          false

        );

 

        ObjectOverrule.Overruling = true;

 

        ed.WriteMessage("\nPreventing erasure of lines.");

      }

      else

      {

        ObjectOverrule.RemoveOverrule(

          RXObject.GetClass(typeof(Line)),

          _theOverrule

        );

 

        _theOverrule.Dispose();

        _theOverrule = null;

 

        ed.WriteMessage("\nNo longer preventing erasure of lines.");

      }

    }

  }

}

The original reason I started investigating this topic was a discussion group post where someone was trying to stop certain blocks from being redefined. Based on the approach of throwing exceptions, I revisited the idea of blocking certain block table records from being opened for write. While it stopped the redefinition from happening, it did (unsurprisingly) cause AutoCAD to crash, which is also why such a technique – while powerful – should be used with extreme caution (hence my comment in the last post describing the act of blocking Erase as being contentious or potentially disastrous). Certain code paths may not be ready for low-level operations within AutoCAD being blocked arbitrarily, especially operations such as opening symbol table records.

There may yet be a simple way to stop block redefinition – if I end up finding a way, I'll certainly post it here.

Update:

I've just seen an internal thread discussing a scenario where blocking erase by throwing ErrorStatus.NotApplicable caused issues with undo. I haven't been able to reproduce the problem, but I did want to post the solution in case it proves to be of help: in this situation returning ErrorStatus.CannotBeErasedByCaller did the trick.

3 responses to “Adding an ObjectOverrule to stop AutoCAD objects from being erased using .NET”

  1. We was researching for solution to our project while unerasing object in Database_ObjectErased caused total crash and using CommandEnded to restore object didnt look like so elegant solution. Thank you again of good blog post!

  2. Hello Kean,

    is there a way to prevent certain objects from being plotted? I know I coud use a Layer for that, but... they should be on the Layer, wich the containing BlockRef is inserted to.
    So I have a 3dPolyline nested in a Block. I need to see it in Modelspace, since it shows a clearence boundary but do not want it to be plotted... (Actually, lot of blocks feature such a line, so simple blockdef modification on Plotcommand would be a performance-killer) I hoped to find an Obejct Property "mypoly.plottable", or so...
    Maybe an Overrule? Is the plot command triggering any event for overrule?

    Thanks in advance,
    Daniel

  3. Hello Daniel,

    There's no direct event, but you might be able to use a DrawableOverrule that nulls out the objects to hide (by not calling the base Worldraw() method) and only gets added when the PLOT command starts.

    Cheers,

    Kean

  4. Subir Kumar Dutta Avatar
    Subir Kumar Dutta

    Hi Kean, In our application user inserts standard blocks from toolpalatte. But after inserting them they can redefine the blocks. How can we restrict user from refining blocks. Can any overrule be applied to blocks so that user can't redefine blocks.

  5. Hi Subir,

    I haven't yet found a workable way to prevent block redefinition, unfortunately.

    Regards,

    Kean

  6. How can I implement a restriction to erase certain kind of polylines.
    Let's say sewer pipes that are indeed polylines with some Xrecord attached to it (diameter, material, installation date, etc).

    I would like to avoid deleting polylines with those attributes but enable deletion of conventional polylines.
    Thanks

  7. Rather than just blindly throwing the exception you can check the state of the object passed in before deciding whether to do so.

    Kean

  8. Thanks Kean.
    It works as expected. Kindest regards.

    Diego

  9. Hi Kean, interesting post, but when I'm trying to erase my object the 'Erase' procedure is called, the exception is thrown, but the code stops here (showing the CannotBeErasedByCaller exception) and the object isn't erased at all. What am I doing wrong?

    1. Hi Luigi,

      Are you using the code as-is? How is it being called?

      Unfortunately I can't tell from your description what's happening.

      Regards,

      Kean

      P.S. If you've made significant changes to the code, please post it to the discussion groups: I unfortunately don't have time to provide support when it's not specifically related to code I've posted.

      1. Ok Kean, the code snippet you provided works fine and I used it "as-is". I realized that if I use the .dll in debug mode the exception is thrown and the editor stops at the line that generates it. But if I use the .dll in release mode, so not hooked to the editor, it works fine and the objects involved are not erased at all, (also translated in VB.NET). So the exception thrown by the row "Throw New Autodesk.AutoCAD.Runtime.Exception(Autodesk.AutoCAD.Runtime.ErrorStatus.NotApplicable (or CannotBeErasedByCaller))" is handled by AutoCAD itself. It should investigate on how to handle the exception in debug mode.

      2. Simply pressing "F8" or "F5" to go ahead with debugging...

        Thank you Kean for sharing.

        Luigi

        1. Ah, of course - the debugger was simply showing an exception was thrown before AutoCAD handled it...

          Glad you were able to work it out.

          Kean

  10. Hello Kean,

    I've implemented an own Attribute-Editor for certain Blocks (triggered by an XDATA-Entry). I needed this to perform Multilanguage-Support for MTEXT-Labels and predefined Attribute Values in given Blocks. The closest method I came to a working solution is to capture the EATTEDIT and BEDIT commands in the "OnEnteringQuiescentState"-Event of the Editor Class. Here I get the "Selection.previous" Objects to process.
    DubleClick-Event did not work out for me...
    In some cases (where there is no previous selection) an error is throwed (and caught by code). Sure I can handle these cases too. Since I'm not fully happy with this implementation (since it's really not elegant đŸ™‚ ), I just meant to ask, if there was a better approach via ObjectOverrule. AFAIK, ObjecOverrule allows to Overrule some predefined Methods only not including the "doubleclick-Action" of the Obejcttype, so I didn't find any handy way till now...

    BR,
    Daniel

    1. Hello Daniel,

      This is pretty off-topic (which means I usually ask people to post to the discussion groups for this kind of question)... but I will suggest this:

      Use CUI to attach the double-click action for the entities you care about, and then do the XData, etc., processing in your command.

      Sorry if I've missed something from your description, but please do post your follow-up to the discussion group, if I have.

      Thanks,

      Kean

      1. Hello Kean,
        Thank you for taking time for me. I already tried the cuix way. However, my intention by this comment was only to get feedback, wether there is a possible solution via Objectoverrule. Is did not ask for support in this manner. I interpret your answer as a no, and this fulfills my request. Thank you again. Br, Daniel

  11. LUIS D´ANDREA Avatar
    LUIS D´ANDREA

    Hi Kean, is it possible to prevent an object from being moved; but still can be rotated?
    I have an object anchored to other (let say the base), and i want to restrict the anchored object so it can only be erased or moved if i move the base, but i can change the relative angle of that anchored object from the base.

    I can't use a dynamic block, because i'm trying to create a custom MEP device (Block looses their dynamic functions once they are part of any AEC object); so i want to combine two devices with overrule to one complex device

    1. Hi Luis,

      If you're using AutoCAD MEP then you can use the OMF to do something like this (I expect), via a custom anchor.

      It's been years (actually decades) since I did anything like this, though, so I can't help with specifics.

      Best,

      Kean

  12. Hello Kean, and thank for the great post!

    My comment arrive a bit late, but I hope it will help somebody. I implemented successfully the EraseOverrule, using the ErrorStatus.NotApplicable which seems to help with the UNDO. However, it breaks my implementation of the Document.CommandEnded and Document.CommandWillStart events. I still don't know why, but each time the EraseOverrule was activated, both events weren't firing anymore and I wasn't even able to hook them up again. I ended up removing them from my code entirely.

    If you have any hints, I would be grateful, but don't stress over it! Thanks again!

    Simon

    1. Hi Simon,

      Unfortunately I'm no longer working with AutoCAD, so really don't have any insights to share. Hopefully someone else here or on the AutoCAD .NET forum can help.

      Best.

      Kean

    2. Hi Simon,

      I think i have solved "UNDO" issue by adding "if (dbObject.IsUndoing) return;" at the beginning of the Erase method.
      It worked fine for me so far.
      I would like to know if this will work for you too, if you still interested.

      public override void Erase(DBObject dbObject, bool erasing)

      {
      if (dbObject.IsUndoing) return;

      throw new Autodesk.AutoCAD.Runtime.Exception(

      Autodesk.AutoCAD.Runtime.ErrorStatus.NotApplicable);

      //base.Erase(dbObject, erasing);

      }

Leave a Reply

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