Preventing the ability to explode a block

Preventing the ability to explode a block

youssefGC
Advocate Advocate
3,054 Views
13 Replies
Message 1 of 14

Preventing the ability to explode a block

youssefGC
Advocate
Advocate

Hi everyone,

I have a question: Is there an effective method to stop a user from decomposing a block? The 'Explodable' property works very well:

using(BlockTableRecord newBlk = new BlockTableRecord())
                {
                    // ......
                    
                    newBlk.Explodable = false;

                    // ......
                }

But, users can later make True  using the block editor. Is it possible to prohibit this possibility?

youssefGC_0-1698301803481.png

 

 

 

0 Likes
Accepted solutions (1)
3,055 Views
13 Replies
Replies (13)
Message 2 of 14

Anton_Huizinga
Advocate
Advocate

You could write a tool that intercept the explode command. But also the same counts here: a user can run AutoCAD without your tool and change the block definition.

 

So, no, it is not possible to prevent this situation.

 

Edit: maybe a little trick, didn't check though. But you could think of adding your block definition as an unnamed block, they are not selectable in the block edit dialog if I remember well, so users are not able to change the block definition (without an enormous effort).

0 Likes
Message 3 of 14

youssefGC
Advocate
Advocate

Thank you for your response.

I have an idea that's been on my mind, but I wasn't sure how to implement it through programming. Is there a method to check if the user has changed the "allow exploding" property?

0 Likes
Message 4 of 14

Norman_Yuan
Mentor
Mentor

In general, I'd rather only set Explodable property of a BlockTableRecord to false, and not worry user changing it in BlockEditor. If a user takes all the trouble to go to BlockEditor to change it, he/she may be good reason to do it, or may not be trained well to follow/comply with the office CAD standard.

 

However, if you really need to use code to add extra measure to prevent it with legitimate reasons, be aware it would only work while your code is loaded:

 

you can handle SystemVariableChanged event to watch system variable "BlockEditor": 0 - block editor is not open, 1 - block editor is open. Then in the event handle, when the block is open, you can

 

1. start handling Database.ObjectModified event and keep track the change blocktablerecord. Once the block editor is closed, you examine if the Explodable property has been changed to true, if yes, you changed it back to false;

 

2. you could also start a custom ObjectOverrule once the block editor opens, which would is used to prevent changes to specific BlockTableRecord (and/or its specific property, such as Explodable). Once the block editor is closed, you disbale the ObjectOverrule.

 

Again, I'd avoid doing such complicated things if possible.

 

 

 

Norman Yuan

Drive CAD With Code

EESignature

Message 5 of 14

youssefGC
Advocate
Advocate

Thank you, Mr. @Norman_Yuan for your ideas.
I tried to implement the second scenario in my case (the code below) by using the system variable "BLOCKEDITOR" to trigger the event that allows me to implement "ObjectOverrule," but unfortunately, I can't control the explosion property.

private static EraseOverrule _instance = null;
        Editor _ed;

        public class EraseOverrule : ObjectOverrule
        {
            public override void Close(DBObject dbObject)
            {
                if (dbObject.IsModified &&
                    !dbObject.IsUndoing &&
                    dbObject.IsWriteEnabled)
                {
                    BlockReference blck = dbObject as BlockReference;

                    if (blck != null)
                    {
                        using (var tr = Application.DocumentManager.MdiActiveDocument.Database.TransactionManager.StartTransaction())
                        {
                            BlockTable acBlkTbl = tr.GetObject(Application.DocumentManager.MdiActiveDocument.Database.BlockTableId, OpenMode.ForRead) as BlockTable;

                            if (acBlkTbl.Has("MDD"))
                            {
                                BlockTableRecord acBlkTblRec = tr.GetObject(acBlkTbl["MDD"], OpenMode.ForWrite) as BlockTableRecord;

                                if (acBlkTblRec.Explodable)
                                {
                                    acBlkTblRec.Explodable = false;
                                }

                            }

                            tr.Commit();
                        }
                    }
                }
                base.Close(dbObject);
            }
        }

        static EraseOverrule _theOverrule = null;

        [CommandMethod("RunMethod1")]
        public void RunMethod1()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            _ed = Application.DocumentManager.MdiActiveDocument.Editor;

            // Subscribe to the SystemVariableChanged event
            Application.SystemVariableChanged +=  new Autodesk.AutoCAD.ApplicationServices.
                     SystemVariableChangedEventHandler(OnEnteringBlockEditor);

        }

        public void OnEnteringBlockEditor(object senderObj,Autodesk.AutoCAD.ApplicationServices.SystemVariableChangedEventArgs sysVarChEvtArgs)
        {
            // Get the current value from a system variable
            int BlkEditorState = System.Convert.ToInt32(Application.GetSystemVariable("BLOCKEDITOR"));

            // If the block editor opened -> start overrule
            if (BlkEditorState==1)
            {
                if (_theOverrule == null)
                {
                    _theOverrule = new EraseOverrule();
                    ObjectOverrule.AddOverrule(RXObject.GetClass(typeof(BlockReference)), _theOverrule, false);
                    ObjectOverrule.Overruling = true;

                    _ed.WriteMessage("\nOverrule Start");
                }
                
            }
            else // If the block editor closed -> end overrule (==0)
            {
                if (_theOverrule != null)
                {
                    ObjectOverrule.RemoveOverrule(RXObject.GetClass(typeof(BlockReference)), _theOverrule);
                    _theOverrule.Dispose();
                    _theOverrule = null;

                    _ed.WriteMessage("\nOverrule End");
                }

            }


        }

 

0 Likes
Message 6 of 14

Norman_Yuan
Mentor
Mentor

Well, after further thought, I realize that handling SystemVariableChanged event to monitor "BlockEditor" variable does not work because the system variable "BlockEditor" does not trigger SystemVariableChanged event: according to the API document: 

 

<QUOTE>

Wraps the AcEditorReactor.sysVarChanged() ObjectARX function. This callback indicates that an attempt was made to change the varName system variable. It does not indicate that the actual value was changed.

 

This callback always occurs when a system variable is changed directly through the SETVAR command or by entering the variable name at the command line. For other built-in AutoCAD commands that provoke system variable changes, this notification is not guaranteed.

</QUOTE>

 

So, you'll need to carefully choose a trigger point to reverse the change to "Explodable" property. In fact, one can choose many different events with Document/Database as the trigger to run the code to reverse the change, it just that your change-reversing code may be trigger to run when it is not necessary (i.e. the target block definition is not changed at all).

 

I just wrote some code for a quick try, aiming to run the change-reversing code only it is likely needed (i.e. after Block Editor is closed). It works as expected: after user opens the Block Editor, change the "Explodabel" to true and close the Block Editor, the code will prompt the user that "changing "Explodable" to true with the block is not allowed, and then the change is reversed.

 

I'll post the full code later today or tomorrow in my blog when I can manage a bit of time. Stay tuned.

Norman Yuan

Drive CAD With Code

EESignature

Message 7 of 14

Norman_Yuan
Mentor
Mentor
Accepted solution
Message 8 of 14

ActivistInvestor
Mentor
Mentor

An Overrule-based solution seems like a good fit for solving that problem:

 

 

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Internal;
using Autodesk.AutoCAD.Runtime;

[assembly: ExtensionApplication(typeof(MyExtensionApplication))]


public abstract class ObjectOverrule<T> : ObjectOverrule where T: DBObject
{
	public ObjectOverrule()
	{
      AddOverrule(RXClass.GetClass(typeof(T)), this, true);
   }

   protected override void Dispose(bool disposing)
   {
      if (disposing && !IsDisposed)
         RemoveOverrule(RXClass.GetClass(typeof(T)), this);
      base.Dispose(disposing);
   }
}

public class NoExplodeBlockOverrule : ObjectOverrule<BlockTableRecord>
{
	string pattern = null;

	public NoExplodeBlockOverrule(string pattern)
	{
		if(string.IsNullOrEmpty(pattern))
			throw new ArgumentException(nameof(pattern));
		this.pattern = pattern;
	}

   public override void Close(DBObject dbObject)
   {
		BlockTableRecord btr = (BlockTableRecord) dbObject;
		if(btr.IsWriteEnabled && btr.Explodable && Utils.WcMatchEx(btr.Name, pattern, true))
			btr.Explodable = false;
      base.Close(dbObject);
   }
}

/////////////////////////////////////////////////////////////////
/// Example usage via IExtensionApplication

public class MyExtensionApplication : IExtensionApplication
{
   NoExplodeBlockOverrule overrule = null;
      
   public void Initialize()
   {
      // specify name(s) of non-explodable
      // block(s) as a wildcard pattern:

      overrule = new NoExplodeBlockOverrule("NONEXPLODABLEBLOCKNAMES");
   }

   public void Terminate()
   {
      overrule.Dispose();
   }
}

 

 

 

Message 9 of 14

ActivistInvestor
Mentor
Mentor

See my post below. Your Overrule is doing way too many things that it shouldn't or can't do which is why it isn't working for you. An Overrule-based solution is actually much simpler than that.

0 Likes
Message 10 of 14

youssefGC
Advocate
Advocate

Thank you very much @Norman_Yuan. I'm testing your code, and it works very well for me. It's simple and convenient, all you need to do is add the name of the block reference inside the code.

 

0 Likes
Message 11 of 14

youssefGC
Advocate
Advocate

Thank you @ActivistInvestor . Yes, I completely agree with you. The code I had posted is not optimized, but I'm doing it in the first place just to test it.

 

0 Likes
Message 12 of 14

ActivistInvestor
Mentor
Mentor

While you are testing, you might want to consider all of the other ways the user has to set the explodable property without  code that watches the use of the block editor detecting it. That's not really a very good way to solve the problem, because it is very easy for an end user to circumvent it.

 

For example, they can use lisp or any other API to set it directly without your code detecting it because you are relying on events that signal the use of the block editor.

 

Even more easily, they could just temporarily rename the block using the RENAME command and then change its explodable property in the block editor, and that code wouldn't detect that either.

 

 

0 Likes
Message 13 of 14

Alexander.Rivilis
Mentor
Mentor

@ActivistInvestor 

IMHO. If block is dynamic and it BlockTableRecord is anonymous - it is useful to prevent to explode not only this BlockTableRecord, but also main BlockTableRecord.

Відповідь корисна? Клікніть на "ВПОДОБАЙКУ" цім повідомленням! | Do you find the posts helpful? "LIKE" these posts!
Находите сообщения полезными? Поставьте "НРАВИТСЯ" этим сообщениям!
На ваше запитання відповіли? Натисніть кнопку "ПРИЙНЯТИ РІШЕННЯ" | Have your question been answered successfully? Click "ACCEPT SOLUTION" button.
На ваш вопрос успешно ответили? Нажмите кнопку "УТВЕРДИТЬ РЕШЕНИЕ"


Alexander Rivilis / Александр Ривилис / Олександр Рівіліс
Programmer & Teacher & Helper / Программист - Учитель - Помощник / Програміст - вчитель - помічник
Facebook | Twitter | LinkedIn
Expert Elite Member

0 Likes
Message 14 of 14

ActivistInvestor
Mentor
Mentor

Yes I agree. I don't know if that's the case with the OP, but Dynamic blocks do make the problem a bit more complex.

 

It depends on if the Explodable property value is propagated to both new and existing anonymous blocks if it is changed on the dynamic block definition.

0 Likes