Author Topic: adding/deleting block attributes  (Read 21575 times)

0 Members and 1 Guest are viewing this topic.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: adding/deleting block attributes
« Reply #15 on: January 30, 2013, 03:37:34 PM »
Let me know if you need this extended to work with dynamic blocks.

Dynamic blocks aside, you'll need probably a call to AttributeReference.AdjustAlignment(Database) somewhere to take care of alignments other than AttachmentPoint.BaseLeft. Then there should be a special case to support multi-line attributes, by testing for IsMTextAttribute/IsMTextAttributeDefinition and calling UpdateMTextAttribute().

Andrey did a recursive Attsync for .Net, with source code here.

This code has been tested in both cases of non standard alignment and mtext attributes and it functions properly.  I remember reading somewhere that the special cases require special support, do you suppose there is something in here that autocad is handling for me? I'm using autocad 2012

Cheers

kaefer

  • Guest
Re: adding/deleting block attributes
« Reply #16 on: January 30, 2013, 04:31:03 PM »
This code has been tested in both cases of non standard alignment and mtext attributes and it functions properly.  I remember reading somewhere that the special cases require special support, do you suppose there is something in here that autocad is handling for me?

If all you're doing is calling SetAttributeFromBlock(), yes, likely it is. I'm thinking that it may be necessary in case anybody fiddles with properties of the new attribute reference.

Just goes to show that I failed to read your code thoroughly. It must have looked just somehow incomplete.

Possible other areas of concern: Did you test with constant attribute definitions? Shouldn't you dispose the cloned attribute definition under all circumstances; in fact, even doing the same with the new attribute reference too?

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: adding/deleting block attributes
« Reply #17 on: January 30, 2013, 06:06:22 PM »
Thank you kaefer! The criticism is appreciated! I have never used a constant attribute so didn't think to handle that case separately.  Turns out a constant attribute does NOT get an attribute reference, so the foreach section of my code needs to be skipped, and a regen must be executed to display the new text.  This complicates the ERASEATTRIBUTE method now, as I filter for an attribute reference... I'll think about how to fix that issue.

Code - C#: [Select]
  1. try
  2.                 {
  3.                     AttributeDefinition ad = (AttributeDefinition)tr.GetObject(adId, OpenMode.ForWrite);
  4.                     BlockReference br = (BlockReference)tr.GetObject(brId, OpenMode.ForRead);
  5.                     BlockTableRecord btr = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForWrite);
  6.                     AttributeDefinition NEWad = (AttributeDefinition)ad.Clone();
  7.                     NEWad.TransformBy(br.BlockTransform.Inverse()); //to set the AD to the BTR coordinate system
  8.                     ObjectId NEWadId = btr.AppendEntity(NEWad);
  9.                     tr.AddNewlyCreatedDBObject(NEWad, true);
  10.                     ObjectIdCollection blockIds = btr.GetBlockReferenceIds(true, true);
  11.                     if (!NEWad.Constant) //constant attributes are NOT added to the block reference
  12.                         foreach (ObjectId id in blockIds) //add attribute reference to all blocks
  13.                         {
  14.                             br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
  15.                             AttributeReference ar = new AttributeReference();
  16.                             ar.SetAttributeFromBlock(NEWad, br.BlockTransform);
  17.                             ar.TextString = ad.TextString;
  18.                             br.AttributeCollection.AppendAttribute(ar);
  19.                             tr.AddNewlyCreatedDBObject(ar, true);
  20.                         }
  21.                     ad.Erase();
  22.                     tr.Commit();
  23.                     ed.Regen();//Regen needed for constant attribute to correctly display
  24.                 }
  25.  

Please correct me if I'm understanding this incorrectly or if I'm losing performance in this method:

-The cloned attribute definition (ad) and the new def (NEWad) get disposed as we leave the scope of the try block (along with br, btr, NEWadId, blockIds etc)
-the attribute reference (ar) gets disposed each time we complete a cycle in the foreach loop

Since this all happens via the garbage collector it's actually being delayed as long as the system does not have an immediate need to reclaim the memory, making the program function slightly faster for the operator.

If anybody can find a way to break this program I'd appreciate it!

Cheers

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: adding/deleting block attributes
« Reply #18 on: January 30, 2013, 06:35:44 PM »
Modified the ERASEATTRIBUTE method to handle constant attributes, turns out they are still just a nested AttributeDefinition

Code - C#: [Select]
  1.                 try
  2.                 {
  3.                     ObjectId arId;
  4.                     System.Type objType;
  5.                     while (true)
  6.                     {
  7.                         PromptEntityResult per = ed.GetNestedEntity(pneo);
  8.                         if (per.Status != PromptStatus.OK) return;
  9.                         arId = per.ObjectId;
  10.                         objType = tr.GetObject(arId, OpenMode.ForRead).GetType();
  11.                         if ((objType == typeof(AttributeReference))|(objType == typeof(AttributeDefinition)))
  12.                             break;
  13.                         ed.WriteMessage("\nNot an Attribute");
  14.                     }
  15.                     if (objType == typeof(AttributeReference))
  16.                     {
  17.                         AttributeReference ar = (AttributeReference)tr.GetObject(arId, OpenMode.ForWrite);
  18.                         BlockReference br = (BlockReference)tr.GetObject(ar.OwnerId, OpenMode.ForRead);
  19.                         BlockTableRecord btr = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForWrite);
  20.                         foreach (ObjectId id in btr)
  21.                         {
  22.                             AttributeDefinition ad = tr.GetObject(id, OpenMode.ForRead) as AttributeDefinition;
  23.                             if (ad == null)
  24.                                 continue;
  25.                             if (ad.Tag != ar.Tag)
  26.                                 continue;
  27.                             ad.UpgradeOpen();
  28.                             ad.Erase();
  29.                         }
  30.                         foreach (ObjectId id in btr.GetBlockReferenceIds(true, true))
  31.                         {
  32.                             br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
  33.                             foreach (ObjectId attId in br.AttributeCollection)
  34.                             {
  35.                                 AttributeReference att = (AttributeReference)tr.GetObject(attId, OpenMode.ForWrite);
  36.                                 if (att.Tag == ar.Tag)
  37.                                 {
  38.                                     att.Erase();
  39.                                 }
  40.                             }
  41.                         }
  42.                     }
  43.                     else
  44.                     {
  45.                         AttributeDefinition att = (AttributeDefinition)tr.GetObject(arId, OpenMode.ForWrite);
  46.                         att.Erase();
  47.                         ed.Regen();
  48.                     }
  49.                     tr.Commit();
  50.                 }
  51.  

This little routine was helpful with investigating what type the nested entity was (as you cant just view its properties  :-()

Code - C#: [Select]
  1.         [CommandMethod("gettype")]
  2.         public void getType()
  3.         {
  4.             Document doc = Application.DocumentManager.MdiActiveDocument;
  5.             Editor ed = doc.Editor;
  6.             Database db = doc.Database;
  7.             PromptNestedEntityOptions pneo = new PromptNestedEntityOptions("\nSelect nested object to get type");
  8.             PromptEntityResult per = ed.GetNestedEntity(pneo);
  9.             if (per.Status != PromptStatus.OK) return;
  10.             using (Transaction tr = db.TransactionManager.StartTransaction())
  11.             {
  12.                 ed.WriteMessage("\nType of object is: {0}", tr.GetObject(per.ObjectId, OpenMode.ForRead).GetType());
  13.             }
  14.         }

Thanks again for pointing that issue out kaefer!

Cheers

Jeff H

  • Needs a day job
  • Posts: 6150
Re: adding/deleting block attributes
« Reply #19 on: January 30, 2013, 06:37:46 PM »
-The cloned attribute definition (ad) and the new def (NEWad) get disposed as we leave the scope of the try block (along with br, btr, NEWadId, blockIds etc)
-the attribute reference (ar) gets disposed each time we complete a cycle in the foreach loop

I think they have disposed called on them during the execution of Transaction.Commit()

btr is known to the transaction because it is opened with the transaction
(ObjectId.GetObject, Transaction.GetObject, .. in the end call TransactionManager.GetObject())

ar & NEWad are also disposed through the TransactionManager and are made known with AddNewlyCreatedDBObject()
If an error occurred after creation of (ar),(NEWad) but before  tr.AddNewlyCreatedDBObject() then there would be a leak in your example.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: adding/deleting block attributes
« Reply #20 on: January 30, 2013, 08:20:19 PM »
-The cloned attribute definition (ad) and the new def (NEWad) get disposed as we leave the scope of the try block (along with br, btr, NEWadId, blockIds etc)
-the attribute reference (ar) gets disposed each time we complete a cycle in the foreach loop

I think they have disposed called on them during the execution of Transaction.Commit()

btr is known to the transaction because it is opened with the transaction
(ObjectId.GetObject, Transaction.GetObject, .. in the end call TransactionManager.GetObject())

ar & NEWad are also disposed through the TransactionManager and are made known with AddNewlyCreatedDBObject()
If an error occurred after creation of (ar),(NEWad) but before  tr.AddNewlyCreatedDBObject() then there would be a leak in your example.

I've added a couple modifications to my code to show that tr.Commit() is not disposing these objects, and used them afterwards to show that they are still completely usable afterwards:

Code - C#: [Select]
  1.         [CommandMethod("TransactionTesting")]
  2.         public void TestTransaction()
  3.         {
  4.             Document doc = Application.DocumentManager.MdiActiveDocument;
  5.             Editor ed = doc.Editor;
  6.             Database db = doc.Database;
  7.             PromptEntityOptions peo = new PromptEntityOptions("\nSelect attribute to add");
  8.             peo.SetRejectMessage("\nNot an AttributeDefinition");
  9.             peo.AddAllowedClass(typeof(AttributeDefinition), true);
  10.             PromptEntityResult per = ed.GetEntity(peo);
  11.             if (per.Status != PromptStatus.OK) return;
  12.             ObjectId adId = per.ObjectId;
  13.             peo = new PromptEntityOptions("\nSelect block to append attribute");
  14.             peo.SetRejectMessage("\nNot a BlockReference");
  15.             peo.AddAllowedClass(typeof(BlockReference), true);
  16.             per = ed.GetEntity(peo);
  17.             if (per.Status != PromptStatus.OK) return;
  18.             ObjectId brId = per.ObjectId;
  19.             AttributeDefinition ad = null;
  20.             BlockReference br = null;
  21.             using (Transaction tr = db.TransactionManager.StartTransaction())
  22.             {
  23.                 try
  24.                 {
  25.                     AttributeDefinition ad1 = (AttributeDefinition)tr.GetObject(adId, OpenMode.ForWrite);
  26.                     BlockReference br1 = (BlockReference)tr.GetObject(brId, OpenMode.ForRead);
  27.                     tr.Commit();
  28.                     ad = ad1;//will fail if disposed at tr.commit()
  29.                     br = br1;
  30.                 }
  31.                 catch (System.Exception e)
  32.                 {
  33.                     ed.WriteMessage("\nError: {0}\n{1}", e.Message, e.StackTrace);
  34.                 }
  35.             }
  36.             using (Transaction tr = db.TransactionManager.StartTransaction())
  37.             {
  38.                 try
  39.                 {
  40.                    
  41.                     BlockTableRecord btr = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForWrite);
  42.                     AttributeDefinition NEWad = (AttributeDefinition)ad.Clone();
  43.                     NEWad.TransformBy(br.BlockTransform.Inverse()); //to set the AD to the BTR coordinate system
  44.                     ObjectId NEWadId = btr.AppendEntity(NEWad);
  45.                     tr.AddNewlyCreatedDBObject(NEWad, true);
  46.                     ObjectIdCollection blockIds = btr.GetBlockReferenceIds(true, true);
  47.                     if (!NEWad.Constant) //constant attributes are NOT added to the block reference
  48.                         foreach (ObjectId id in blockIds) //add attribute reference to all blocks
  49.                         {
  50.                             br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
  51.                             AttributeReference ar = new AttributeReference();
  52.                             ar.SetAttributeFromBlock(NEWad, br.BlockTransform);
  53.                             ar.TextString = ad.TextString;
  54.                             br.AttributeCollection.AppendAttribute(ar);
  55.                             tr.AddNewlyCreatedDBObject(ar, true);
  56.                         }
  57.                     ad.Erase();
  58.                     tr.Commit();
  59.                     ed.Regen();//Regen needed for constant attribute to correctly display
  60.                 }
  61.                 catch (System.Exception e)
  62.                 {
  63.                     ed.WriteMessage("\nError: {0}\n{1}", e.Message, e.StackTrace);
  64.                 }
  65.             }
  66.             BlockReference br2 = (BlockReference)br.Clone();//cloning outside of a transaction works
  67.             using (Transaction tr = db.TransactionManager.StartTransaction())
  68.             {
  69.                
  70.                 AttributeDefinition ad1 = new AttributeDefinition();
  71.                 ad1.SetDatabaseDefaults();
  72.                 ad1.Tag = "tag";
  73.                 ad1.TextString = "textString";
  74.                 ad1.Constant = true;
  75.                 Matrix3d mat = br2.BlockTransform;
  76.                 ad1.TransformBy(mat);
  77.                 BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  78.                 btr.AppendEntity(ad1);
  79.                 tr.AddNewlyCreatedDBObject(ad1, true);
  80.                tr.Commit();
  81.                 ed.Regen();
  82.             }
  83.         }
  84.  

Jeff H

  • Needs a day job
  • Posts: 6150
Re: adding/deleting block attributes
« Reply #21 on: January 30, 2013, 10:20:59 PM »

********************************EDIT************************
On second look I am not sure I thought clone was working because using GetObject was setting autodelete to false and the UmanagedObject was not getting
set to IntPtr.Zero, but I don't know and would have to look into it futher.
********************************EDIT************************



I guess the question is what does disposed mean?
I meant dispose has been called on the managed wrapper and the native object has been closed( AcDbObject.close)
During Commit() is probably not correct, but when the transaction is closed or if its nested when topmost transaction is closed.

Quote
ad = ad1;//will fail if disposed at tr.commit()
Assigning ad = ad1 should work fine as long as ad1 and ad are compatible types.
 
So when i said disposed I meant Dispose() implemented by IDisposable has been called.

When calling clone
Code - C#: [Select]
  1.  
  2. public virtual unsafe object Clone()
  3. {
  4.     AcRxObject* impObj = this.GetImpObj();
  5.     IntPtr unmanagedPointer = new IntPtr((void*) **(((long*) impObj))[0x10L](impObj));
  6.     return Create(unmanagedPointer, true);
  7. }
  8.  

The (IntPtr)UnmanagedObject property was still valid to create a new managed wrapper.


Are you not getting an error at ad.Erase()?

« Last Edit: January 30, 2013, 10:38:03 PM by Jeff H »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: adding/deleting block attributes
« Reply #22 on: January 30, 2013, 11:15:18 PM »
Nope, no errors. As far as I can tell since the objects are resident to a database present in ram the managed class still holds the pointer to them. Unless we call dispose manually I think they'll live till the garbage collector cleans up.  There are no errors provided we don't attempt to perform operations that must be transaction resident.
Cheers

cadplayer

  • Bull Frog
  • Posts: 390
  • Autocad Civil3d, OpenDCL.Runtime, LISP, .NET (C#)
Re: adding/deleting block attributes
« Reply #23 on: January 31, 2013, 03:49:36 AM »
Thanks boys fantastic work! /Dirk

Hugo

  • Bull Frog
  • Posts: 431
Re: adding/deleting block attributes
« Reply #24 on: January 31, 2013, 07:46:14 AM »
What should I shop here

Was muss ich hier Laden

Quote
Der Typ- oder Namespacename "Matrix3d" konnte nicht gefunden werden. (Fehlt eine Using-Direktive oder ein Assemblyverweis?)

Danke

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: adding/deleting block attributes
« Reply #25 on: January 31, 2013, 09:42:13 AM »
Die Matrix3D in Autodesk.Autocad.Geometry Namespace enthalten. Es ist in der Regel recht einfach durch googeln Autocad Matrix3D (oder eine andere Klasse, die Sie kann gesucht werden) finden Namespace
 Erhalten auch die ARX-Dokumentation und den Namespace aller Klassen werden zusammen mit all ihren Methoden gezeigt. Sie haben eine einfachere Zeit bekommen Antworten, wenn Sie Ihre Kommentare übersetzen ...
Prost

Hugo

  • Bull Frog
  • Posts: 431
Re: adding/deleting block attributes
« Reply #26 on: February 01, 2013, 12:06:22 AM »
now I get this error message

jetzt bekomme ich diese Fehlermeldung


Jeff H

  • Needs a day job
  • Posts: 6150
Re: adding/deleting block attributes
« Reply #27 on: February 01, 2013, 12:55:55 AM »
Probably at ad.Erase() since it gets closed when the first transaction ends.


 

Hugo

  • Bull Frog
  • Posts: 431
Re: adding/deleting block attributes
« Reply #28 on: February 01, 2013, 02:10:08 AM »
Hello Jeff H

The wahrs now it works great 1000 times thanks  :-) :-) :-)


Hallo Jeff H

Das wahrs jetzt funktioniert es, super 1000 mal Danke


Now would be even great for ATTSYNC a program without moving the attributes.
Jetzt wäre noch toll ein  Programm für ATTSYNC ohne die Attribute zu verschieben.
« Last Edit: February 01, 2013, 02:32:36 AM by Hugo »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: adding/deleting block attributes
« Reply #29 on: February 01, 2013, 10:45:43 AM »
Hugo, Jeff,

What version are you using?  I'm trying to wrap my head around why things are working here if they object is apparently closed with the transaction  :-o.  If this is something version dependent then my code will be failing on different versions of AutoCAD... I can see how it would make logical sense that as the transaction was disposed the objects opened for write would be downgraded to read only to prevent any modification later.  Can anybody explain what is going on here?

Cheers