.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Prevent erase

10 REPLIES 10
Reply
Message 1 of 11
stuartnathan
1975 Views, 10 Replies

Prevent erase

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

10 REPLIES 10
Message 2 of 11
norman.yuan
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/

Message 3 of 11
stuartnathan
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

Message 4 of 11
norman.yuan
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!

 

Message 5 of 11
stuartnathan
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.

Message 6 of 11
norman.yuan
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.

 

Message 7 of 11
stuartnathan
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

 

Message 8 of 11
stuartnathan
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)

 

Message 9 of 11
norman.yuan
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.

Message 10 of 11
stuartnathan
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)

 

Message 11 of 11
BlackBox_
in reply to: norman.yuan


@norman.yuan wrote:

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).

 

<snip>

 

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.


Just stumbled upon this old post, and your implementation using SetCustomFilter() and IsApplicable() within your ObjectOverrule (mine used within an OsnapOverrule for a completely different purpose) is exactly what I was after - thank you! :beer:

 

Cheers 



"How we think determines what we do, and what we do determines what we get."

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost