.NET

Reply
Valued Contributor
Posts: 70
Registered: ‎02-17-2005
Message 1 of 10 (598 Views)

Prevent erase

598 Views, 9 Replies
01-10-2012 03:09 AM

Before asking this, I have looked everywhere without success.

I have a title sheet which I want to prevent being erased if certain conditions are met.

My code is:-

Friend Sub ObjectErased(ByVal sender As Object, ByVal e As Autodesk.AutoCAD.DatabaseServices.ObjectErasedEventArgs)
      Select Case e.DBObject.Id.ObjectClass.Name
         Case "AcDbBlockReference"
            Using tr As Transaction = AcadDoc.TransactionManager.StartTransaction()
               Dim blk As BlockReference = CType(e.DBObject, BlockReference)
               Dim ac As AttributeCollection = blk.AttributeCollection
               If ac.Count > 0 Then
                  For Each ad As ObjectId In ac
                     Dim ar As AttributeReference = ad.GetObject(OpenMode.ForRead)
                     If ar.Tag ="NO" Then
                        blk.UpgradeOpen()                                        '  ensure opened for write
                        blk.Erase(False)                                            '  prevent deletion
                        Exit For
                     End If
                  Next
               End If
            End Using
      End Select
   End Sub

 I had hoped that blk.Erase(False) would prevent the deletion, but it doesn't.

 

Can someone explain what I am doing wrong?

 

I have since added a tr.commit before exit for - no difference

*Expert Elite*
Posts: 896
Registered: ‎04-27-2009
Message 2 of 10 (581 Views)

Re: Prevent erase

01-10-2012 06:55 AM in reply to: stuartnathan

"Looked everywhere"?

 

How about the well-known blog "through-the-interface" of Kean Walmsley? Check out this post of his:

 

http://through-the-interface.typepad.com/through_the_interface/overrules/

Valued Contributor
Posts: 70
Registered: ‎02-17-2005
Message 3 of 10 (575 Views)

Re: Prevent erase

01-10-2012 08:40 AM in reply to: norman.yuan

Looked at blog before and I felt it might be too dangerous (going into areas I didn't understand).

However had another look and it is starting to work.

Now I have:-

      Public Overrides Sub [Erase](ByVal obj As DBObject, ByVal erasing As Boolean)
         MyBase.[Erase](obj, erasing)
         If TypeOf obj Is BlockReference Then
            Dim blk As BlockReference = CType(obj, BlockReference)
            Dim ac As AttributeCollection = blk.AttributeCollection
            If ac.Count > 0 Then
               For Each ad As ObjectId In ac
                  Dim ar As AttributeReference = ad.GetObject(OpenMode.ForRead)
                  If ar.Tag = "NO" Then
                     Throw New Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NotApplicable)
                     Exit For
                  End If
               Next
            End If
         End If
      End Sub
   End Class

 but

Dim ar As AttributeReference = ad.GetObject(OpenMode.ForRead)

crashes AutoCad with message

FATAL ERROR: Unhandled Access Violation Reading 0x0000 Exception at ...

 

Thanks for pointer though



*Expert Elite*
Posts: 896
Registered: ‎04-27-2009
Message 4 of 10 (568 Views)

Re: Prevent erase

01-10-2012 08:56 AM in reply to: stuartnathan

Did the crash occur in debugging session or normal Acad session?

 

The solution of preventing entity being erased in Kean's code is good in normal Acad session, but it is not "debug-able", because the solution is to raise an exception to stop erasing. Custom code cannot catch and handle this exception, or the erase would not be stopped. Thus, if you run the code ini debugging session, Acad always crash at the line when the exception is thrown.

 

Since this is the way the API is implemented, I do not know if there is a way to around it. So, if you debug your code, you do not want to try erase the title block with your overrule loaded. On the other hand, you you do try to erase the title block and Acad crash at the line of throwing exception in the debugging session, then it is good thing: your code will work in normal Acad session!

 

Valued Contributor
Posts: 70
Registered: ‎02-17-2005
Message 5 of 10 (562 Views)

Re: Prevent erase

01-10-2012 10:04 AM in reply to: norman.yuan

As I said before the crash occured on the line

Dim ar As AttributeReference = ad.GetObject(OpenMode.ForRead)

not

Throw New Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.Not​Applicable)

 

I was in debug mode to see this.

 

If I am not in debug mode, AutoCad pauses for some time and then crashes.

*Expert Elite*
Posts: 896
Registered: ‎04-27-2009
Message 6 of 10 (557 Views)

Re: Prevent erase

01-10-2012 10:17 AM in reply to: stuartnathan

OK. I think it is probably due to the fact that you called MyBase.Erase() first, then try to identify if the entity being erased is your iverrule target. At the time, the entity may have been already flagged as "erased" of "effectivelyErased", so you cannot open it anymore.

 

So, you could try to move the MyBase.Erase() right in front of "Throw New....".

 

However, I'd not place the logic of finding overrule target in overriden Erase(0 method. I'd override IsApplicable() to define my own overrule filter, so that only intended targeting entity (in your case, the title block) is subject the overrule. That is, the logic of identifying overrule target is in IsApplicable() method, actual logic to stop erasing is in Erase() method, which is as simple as 2 lines of code:

 

MyBase.Erase()

Throw New ....

 

shown as Kean's blog.

 

Valued Contributor
Posts: 70
Registered: ‎02-17-2005
Message 7 of 10 (553 Views)

Re: Prevent erase

01-10-2012 10:45 AM in reply to: norman.yuan

I don't think is is myBase.Erase

I use Overrides quite often and I put the myBase etc at the end of the Sub, which I then did here. It still crashed on the

Dim ar at attribute

 

Valued Contributor
Posts: 70
Registered: ‎02-17-2005
Message 8 of 10 (552 Views)

Re: Prevent erase

01-10-2012 11:05 AM in reply to: norman.yuan

I don't understand your answer.

I now added:-

      Public Overrides Function IsApplicable(ByVal overruledSubject As Autodesk.AutoCAD.Runtime.RXObject) As Boolean
         AcadEdt.WriteMessage(overruledSubject.ToString)
         Return MyBase.IsApplicable(overruledSubject)
      End Function

 But it doesn't come here.

 

Can you also explain what the [..] mean in

Public Overrides Sub [Erase](ByVal obj As DBObject, ByVal erasing As Boolean)

 

*Expert Elite*
Posts: 896
Registered: ‎04-27-2009
Message 9 of 10 (546 Views)

Re: Prevent erase

01-10-2012 02:00 PM in reply to: stuartnathan

You need a bit study on Overrule's filter. In your case, you want to define your own custon filter: only block reference with given name or with given attribute tag is subject to your ObjectOverrule (cannot be erased).

 

I put together some quick code here:

 

using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;

namespace StopBlockErasing
{
    public class NonErasableBlockOverule : ObjectOverrule
    {
        private bool _originalOverruling;
        private List<string> _blockNames;

        public NonErasableBlockOverule()
        {
            //Get a block name list from settiings
            _blockNames = new List<string>();
            _blockNames.AddRange(
                new string[] { "Block1", "Block2", "Block3" }
                );
        }

        public void StartOverruling()
        {
            //Save current overruling status (on/off)
            _originalOverruling = Overrule.Overruling;

            this.SetCustomFilter();

            //Add this overule
            Overrule.AddOverrule(RXObject.GetClass(typeof(BlockReference)), this, true);
            Overrule.Overruling = true;
        }

        public void StopOverruling()
        {
            Overrule.RemoveOverrule(RXObject.GetClass(typeof(BlockReference)), this);

            //Restore original overruling status
            Overrule.Overruling = _originalOverruling;
        }

        //Implement overrule custom filter: only blockreference with
        //give name is applicable
        public override bool IsApplicable(RXObject overruledSubject)
        {
            BlockReference bref = overruledSubject as BlockReference;
            if (bref == null) return false;

            return IsTargetBlock(bref.Name);
        }

        public override void Erase(DBObject dbObject, bool erasing)
        {
            base.Erase(dbObject, erasing);
            throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NotApplicable);
        }

        #region private methods

        private bool IsTargetBlock(string blkName)
        {
            foreach (string b in _blockNames)
            {
                if (b.ToUpper() == blkName.ToUpper()) return true;
            }

            return false;
        }

        #endregion

        //------------------------------------------------------------
        //  Another version of IsApplication() implementing
        //  If a given attribute tag presents in the block
        //------------------------------------------------------------
        //public override bool IsApplicable(RXObject overruledSubject)
        //{
        //    BlockReference bref = overruledSubject as BlockReference;
        //    if (bref == null) return false;

        //    //if we need to go through attribute to determine 
        //    //whether the block should be erased or not
        //    if (bref.AttributeCollection.Count == 0) return false;

        //    Database db = bref.Database;
        //    using (Transaction tran = db.TransactionManager.StartTransaction())
        //    {
        //        foreach (ObjectId id in bref.AttributeCollection)
        //        {
        //            AttributeReference aref = tran.GetObject(
        //                id, OpenMode.ForRead) as AttributeReference;
        //            if (aref != null)
        //            {
        //                //Only remove a block, if one of its attribute tag is "AAA"
        //                if (aref.Tag.ToUpper() == "AAA") return true;
        //            }
        //        }
        //    }

        //    return false;
        //}
    }
}

 Here is the code to run it:

 

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;

[assembly: CommandClass(typeof(StopBlockErasing.MyCommands))]

namespace StopBlockErasing
{
    class MyCommands
    {
        private static bool _blockOverruling = false;
        private static NonErasableBlockOverule _myOverrule=null;

        [CommandMethod("BlkOverrule")]
        public static void MyCmd()
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;

            if (_myOverrule == null) _myOverrule = new NonErasableBlockOverule();

            if (_blockOverruling)
            {
                _myOverrule.StopOverruling();
                _blockOverruling=false;
                ed.WriteMessage("\nNonErasableBlock Overrule is turned off.\n");
            }
            else
            {
                _myOverrule.StartOverruling();
                _blockOverruling = true;
                ed.WriteMessage("\nNonErasableBlock Overrule is turned on.\n");
            }
            
        }
    }
}

 The code is in C#, but I think it would be easy to figure its VB.NET equivalent easily.

 

The IsApplicable is implemented this way: as long as the block name is "Block1", Block2" or "Block3", then the block cannot be erased. The extra IsApplicable() being commented out at the bottom is to check if a block has an attribute with Tag named as "AAA", if yes, the block is not erasable.

 

Notice the line:

 

this.SetCustomFilter();

 

You must call this method in order to tell Overrule to use your overriden IsApplicable() as custom filter.

 

I did run the code with my Acad2012 successfully (of make some block not erasable).

 

HTH.

Valued Contributor
Posts: 70
Registered: ‎02-17-2005
Message 10 of 10 (542 Views)

Re: Prevent erase

01-10-2012 03:01 PM in reply to: norman.yuan

Thankyou for what was clearly a lot of hard work. I keep getting errors when trying to convert to VB, and so I will do it manually. However looking at your code made me think a little more and I got the answer.

I changed the line

 

                     Dim ar As AttributeReference = CType(ad.GetObject(OpenMode.ForRead), AttributeReference)

 to using the Transaction - tr

 

                     Dim ar As AttributeReference = CType(tr.GetObject(arId, OpenMode.ForRead), AttributeReference)

 

You are not logged in.

Log into access your profile, ask and answer questions, share ideas and more. Haven't signed up yet? Register

Announcements
Are you interested in helping shape the Autodesk Community?
We’re looking at a few different ways to improve the “All Forums” landing page and need your feedback! If interested, please take a few minutes to fill out the following Usability Study. Thank you for your time!

Need installation help?

Start with some of our most frequented solutions to get help installing your software.

Ask the Community