Author Topic: Attribute prompt from AttributeReference ?  (Read 3071 times)

0 Members and 1 Guest are viewing this topic.

latour_g

  • Newt
  • Posts: 184
Attribute prompt from AttributeReference ?
« on: March 13, 2018, 10:17:07 AM »
Hi,
I have some weird block that contains duplicates attributes.  They are not visible in the blockdefinition.  The only place I see them is in block reference itself and the only way to recognize the duplicate attribute is that they have no prompt.  So is there a way to get the prompt propriety of an attribute reference ?
Thank you

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Attribute prompt from AttributeReference ?
« Reply #1 on: March 13, 2018, 11:25:08 AM »
Hi,

AFAIK there's no way, the prompt only exists in the attribute definition.

An attribute synchronization should remove these duplicated attribute references. You can alson try a "selective attsync".
Speaking English as a French Frog

latour_g

  • Newt
  • Posts: 184
Re: Attribute prompt from AttributeReference ?
« Reply #2 on: March 13, 2018, 03:54:50 PM »
attsync work great but it remove value of the attributes so it's not a solution for me.
I can't use selective attsync because the duplicate attribute are identical.
 
But it gave me an idea :
1 - I select the block and make a sorted list out of it (SortedList<ObjectId, string>) where ObjectId = ObjectId of the block reference and string = value of attribute
2 - I use attsync, after that all look great
3 - To put back value in attribute, I use my sortedlist
The problem is at step 3 I got an error message that my attribute reference is erase.  I don't understand why... the error is for idAtt at line 22.  I see that the count of blkRef.AttributeCollection is 2 instead of 4.

Code - C#: [Select]
  1.             Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
  2.             Database db = doc.Database;
  3.             Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;
  4.  
  5.             using (doc.LockDocument())
  6.             {
  7.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  8.                 {
  9.                     foreach(KeyValuePair<ObjectId, string> pair in listBlkAtt)
  10.                     {
  11.                         ObjectId idBlk = pair.Key;
  12.                         string cValue = pair.Value;
  13.  
  14.                         string[] cCircuitStatut = cValue.Split('+');
  15.                         string cCircuit = cCircuitStatut[0];
  16.                         string cStatut = cCircuitStatut[1];
  17.  
  18.                         BlockReference blkRef = (BlockReference)tr.GetObject(idBlk, OpenMode.ForWrite);                        
  19.  
  20.                         foreach (ObjectId idAtt in blkRef.AttributeCollection)
  21.                         {
  22.                             AttributeReference att = (AttributeReference)tr.GetObject(idAtt, OpenMode.ForWrite);
  23.                             if (att.Tag == "CIRCUIT") att.TextString = cCircuit;
  24.                             else if (att.Tag == "STATUT") att.TextString = cStatut;
  25.                         }
  26.                     }
  27.                     tr.Commit();
  28.                 }
  29.             }
  30.         }

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Attribute prompt from AttributeReference ?
« Reply #3 on: March 13, 2018, 07:59:21 PM »
I think you've forgotten that AutoCAD has tools like this ;) ATTOUT and ATTIN make it easy to save the attribute values that are being modified by attsync.

Have you looked at these objects with MGDDBG? It seems really odd that there's only two IDs showing up in the AttributeCollection, that would be a good way to look at them.

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: Attribute prompt from AttributeReference ?
« Reply #4 on: March 13, 2018, 10:08:38 PM »
ATTSYNC will delete any Attribute Reference tags that don't match any Definition tags, that is why your AttributeReference was erased in step 3. As Will said, use ATTOUT them to preserve the values before you get destructive with ATTSYNC. You can change the column headers in excel to the new tags before you do ATTIN and you will get your values back. Just watch out for Excel's automatic formatting when you open the exported file, format all the columns at Text

Also confirming you can only get the Attribute prompt from the Definition, the Reference does not have it. Changing the Prompt won't break anything or lose any values. Note that changing an Attribute Tag in a block definition will erase its value in all references if the references no longer have a matching tag - that is why you use nanananana, BATTMAN to edit attribute tags. Programatically, if you change an Tag in a block definition then you need to iterate all its block references and make the same change to preserve the values.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Attribute prompt from AttributeReference ?
« Reply #5 on: March 14, 2018, 02:42:38 AM »
You can try these extension methods:

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using Autodesk.AutoCAD.Runtime;
  4. using AcRx = Autodesk.AutoCAD.Runtime;
  5.  
  6. namespace Autodesk.AutoCAD.DatabaseServices
  7. {
  8.     public static class ExtensionMethods
  9.     {
  10.         static RXClass attDefClass = RXClass.GetClass(typeof(AttributeDefinition));
  11.  
  12.         public static void SynchronizeAttributes(this BlockTableRecord target)
  13.         {
  14.             if (target == null)
  15.                 throw new ArgumentNullException("target");
  16.  
  17.             Transaction tr = target.Database.TransactionManager.TopTransaction;
  18.             if (tr == null)
  19.                 throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
  20.  
  21.             List<AttributeDefinition> attDefs = target.GetAttributeDefinitions();
  22.             foreach (ObjectId id in target.GetBlockReferenceIds(true, false))
  23.             {
  24.                 BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
  25.                 br.ResetAttributes(attDefs, tr);
  26.             }
  27.             if (target.IsDynamicBlock)
  28.             {
  29.                 target.UpdateAnonymousBlocks();
  30.                 foreach (ObjectId id in target.GetAnonymousBlockIds())
  31.                 {
  32.                     BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
  33.                     attDefs = btr.GetAttributeDefinitions();
  34.                     foreach (ObjectId brId in btr.GetBlockReferenceIds(true, false))
  35.                     {
  36.                         BlockReference br = (BlockReference)tr.GetObject(brId, OpenMode.ForWrite);
  37.                         br.ResetAttributes(attDefs, tr);
  38.                     }
  39.                 }
  40.             }
  41.         }
  42.  
  43.         public static List<AttributeDefinition> GetAttributeDefinitions(this BlockTableRecord target)
  44.         {
  45.             if (target == null)
  46.                 throw new ArgumentNullException("target");
  47.  
  48.             Transaction tr = target.Database.TransactionManager.TopTransaction;
  49.             if (tr == null)
  50.                 throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
  51.  
  52.             List<AttributeDefinition> attDefs = new List<AttributeDefinition>();
  53.             foreach (ObjectId id in target)
  54.             {
  55.                 if (id.ObjectClass == attDefClass)
  56.                 {
  57.                     AttributeDefinition attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  58.                     attDefs.Add(attDef);
  59.                 }
  60.             }
  61.             return attDefs;
  62.         }
  63.  
  64.         private static void ResetAttributes(this BlockReference br, List<AttributeDefinition> attDefs, Transaction tr)
  65.         {
  66.             Dictionary<string, string> attValues = new Dictionary<string, string>();
  67.             foreach (ObjectId id in br.AttributeCollection)
  68.             {
  69.                 if (!id.IsErased)
  70.                 {
  71.                     AttributeReference attRef = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite);
  72.                     if (!attValues.ContainsKey(attRef.Tag))
  73.                     {
  74.                         attValues.Add(attRef.Tag,
  75.                             attRef.IsMTextAttribute ? attRef.MTextAttribute.Contents : attRef.TextString);
  76.                     }
  77.                     attRef.Erase();
  78.                 }
  79.             }
  80.             foreach (AttributeDefinition attDef in attDefs)
  81.             {
  82.                 AttributeReference attRef = new AttributeReference();
  83.                 attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  84.                 if (attDef.Constant)
  85.                 {
  86.                     attRef.TextString = attDef.IsMTextAttributeDefinition ?
  87.                         attDef.MTextAttributeDefinition.Contents :
  88.                         attDef.TextString;
  89.                 }
  90.                 else if (attValues.ContainsKey(attRef.Tag))
  91.                 {
  92.                     attRef.TextString = attValues[attRef.Tag];
  93.                 }
  94.                 br.AttributeCollection.AppendAttribute(attRef);
  95.                 tr.AddNewlyCreatedDBObject(attRef, true);
  96.             }
  97.         }
  98.     }
  99. }
  100.  
Speaking English as a French Frog

latour_g

  • Newt
  • Posts: 184
Re: Attribute prompt from AttributeReference ?
« Reply #6 on: March 14, 2018, 11:35:44 AM »
I didn't know about ATTOUT/ATTIN, that might work, I will do some test.

Thanks to the three of you for all these informations !