Author Topic: inserting dynamic block by name, or reading dyn props (with example)  (Read 5392 times)

0 Members and 1 Guest are viewing this topic.

nekitip

  • Guest
When we talk about blocks, there are a lof of examples online, and here is an example of both reading and writing dynamic block with attributes (by name).
However, my problems are:
-how to read dyn props without crashing (I'm missing something, but I do not know what)
-how to make attributes position themself on right position when we change visuals of blockref from code
-what for are QueueForGraphicsFlush and other commented stuff

Code - vb.net: [Select]
  1. Imports System
  2. Imports Autodesk.AutoCAD.Runtime
  3. Imports Autodesk.AutoCAD.ApplicationServices
  4. Imports Autodesk.AutoCAD.DatabaseServices
  5. Imports Autodesk.AutoCAD.Geometry
  6. Imports Autodesk.AutoCAD.EditorInput
  7.  
  8. <Assembly: CommandClass(GetType(test1.MyCommands))>
  9.  
  10. Namespace test1
  11.  
  12.  
  13.     Public Class MyCommands
  14.  
  15. #Region "commands"
  16.  
  17.         <CommandMethod("INSERTDYNAMICBLOCK")>
  18.         Public Shared Sub insertBlock()
  19.             Dim attList As New List(Of String)
  20.             Dim name As String = "RMNUM"
  21.  
  22.             Dim db As Database = HostApplicationServices.WorkingDatabase()
  23.             Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.GetDocument(db)
  24.             Dim ed As Editor = doc.Editor
  25.  
  26.             Using trans As Transaction = db.TransactionManager.StartTransaction()
  27.  
  28.                 ' what is this for?
  29.                 'doc.TransactionManager.EnableGraphicsFlush(True)
  30.  
  31.                 Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead)
  32.                 Dim btr As BlockTableRecord = trans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite)
  33.  
  34.                 'what is this for?
  35.                 'Dim occ As ObjectContextCollection = db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES")
  36.  
  37.                 Dim blockDef As BlockTableRecord = trans.GetObject(bt(name), OpenMode.ForRead)
  38.                 'if bt.has...then
  39.                 Using acNewBlockRef As New BlockReference(New Point3d, blockDef.ObjectId)
  40.  
  41.                     btr.AppendEntity(acNewBlockRef)
  42.                     trans.AddNewlyCreatedDBObject(acNewBlockRef, True)
  43.  
  44.  
  45.                     'attributes
  46.                     'classic attrib must always have tag, but tag can be the same
  47.                     'dynamic prop name must be different
  48. 'HERE: BLOCK ATTRIBUTES ARE NOT TRANSFORMED WHEN CHANGING DP (or am I missing something simple somewhere)
  49.                     If acNewBlockRef.IsDynamicBlock Then
  50.                         For Each dp As DynamicBlockReferenceProperty In acNewBlockRef.DynamicBlockReferencePropertyCollection
  51.                             If Not dp.ReadOnly Then
  52.                                 'now, IF we change dynprop (flip), then attrib is no longer at proper place without attsync
  53.                                 Dim dpnameToSearch As String = "Flip state1"
  54.                                 Dim newVal As Double = 1.0
  55.  
  56.                                 Select Case dp.PropertyTypeCode
  57.                                     Case 1
  58.                                         dp.Value = newVal
  59.                                     Case 2
  60.                                         dp.Value = Convert.ToInt32(newVal Mod Int32.MaxValue)
  61.                                     Case 3
  62.                                         dp.Value = Convert.ToInt16(newVal Mod Int16.MaxValue)
  63.                                     Case 4
  64.                                         dp.Value = Convert.ToSByte(newVal Mod Byte.MaxValue)
  65.                                     Case 5
  66.                                         dp.Value = Convert.ToString(newVal)
  67.                                     Case 13
  68.                                         dp.Value = Convert.ToInt64(newVal Mod Int64.MaxValue)
  69.                                     Case Else
  70.                                 End Select
  71.                             End If
  72.  
  73.                         Next
  74.                     End If
  75.  
  76.  
  77.  
  78.                     For Each attId As ObjectId In blockDef
  79.                         Dim ent As Entity = trans.GetObject(attId, OpenMode.ForRead)
  80.                         If TypeOf ent Is AttributeDefinition Then
  81.                             Dim attDef As AttributeDefinition = DirectCast(ent, AttributeDefinition)
  82.  
  83.                             If (attDef IsNot Nothing) AndAlso (Not attDef.Constant) Then
  84.                                 'This is a non-constant AttributeDefinition
  85.                                 Using attRef As New AttributeReference()
  86.  
  87.                                     attRef.SetAttributeFromBlock(attDef, acNewBlockRef.BlockTransform)
  88.                                     acNewBlockRef.AttributeCollection.AppendAttribute(attRef)
  89.                                     trans.AddNewlyCreatedDBObject(attRef, True)
  90.  
  91.                                     'CHECK HERE FOR THIS ATTRIB
  92.  
  93.                                     attRef.TextString = "test"
  94.                                     If attRef.HasFields Then
  95.                                         Dim fOif As ObjectId = attRef.GetField()
  96.                                         Dim fo As Field = trans.GetObject(fOif, OpenMode.ForWrite)
  97.                                         fo.Evaluate()
  98.                                     End If
  99.  
  100.                                 End Using
  101.                             End If
  102.                         End If
  103.                     Next
  104.  
  105.                     'what is this for?
  106.                     'acNewBlockRef.RecordGraphicsModified(True) ' to force updating a block reference
  107.  
  108.                 End Using
  109.  
  110.                 'what is this for?
  111.                 'trans.TransactionManager.QueueForGraphicsFlush()
  112.  
  113.                 trans.Commit()
  114.             End Using
  115.         End Sub
  116.  
  117.         <CommandMethod("GETBLOCKATT")>
  118.         Public Shared Sub getBlockAttributesByBlockName(g)
  119.  
  120.             Dim name As String = "RMNUM"
  121.             Dim realName As String = ""
  122.             realName = name
  123.  
  124.             Dim attList As New List(Of String)
  125.  
  126.             Dim db As Database = HostApplicationServices.WorkingDatabase()
  127.             Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.GetDocument(db)
  128.             Dim ed As Editor = doc.Editor
  129.  
  130.             Using trans As Transaction = db.TransactionManager.StartTransaction()
  131.  
  132.                 Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead)
  133.                 If bt.Has(name) Then
  134.  
  135.                     Dim blockDef As BlockTableRecord = trans.GetObject(bt(name), OpenMode.ForRead)
  136.                     realName = blockDef.Name
  137.                     If blockDef.HasAttributeDefinitions Then
  138.                         For Each chkObjID As ObjectId In blockDef
  139.                             Dim ent As Entity = trans.GetObject(chkObjID, OpenMode.ForRead)
  140.                             If TypeOf ent Is AttributeDefinition Then
  141.                                 Dim attDef As AttributeDefinition = DirectCast(ent, AttributeDefinition)
  142.                                 attList.Add(attDef.Tag)
  143.                             End If
  144.                         Next
  145.                     End If
  146.  
  147.                     If blockDef.IsDynamicBlock Then
  148. 'HERE: CRASH IN 50% TIMES
  149.                         'create new temp ref since i do now know how to get all dynprops
  150.                         'here, something is wrong. could be that there are more anonymous block created and they are not deleted with dispose
  151.                         'how to fix this? Or, how to find dynprop withot creating new ref?
  152.                         Dim tempDynBlockRef As New BlockReference(New Point3d, blockDef.ObjectId)
  153.  
  154.                         For Each dp As DynamicBlockReferenceProperty In tempDynBlockRef.DynamicBlockReferencePropertyCollection
  155.                             If Not dp.ReadOnly Then attList.Add(dp.PropertyName)
  156.                         Next
  157.  
  158.                         tempDynBlockRef.Dispose()
  159.  
  160.                     End If
  161.                 End If
  162.                 trans.Commit()
  163.             End Using
  164.  
  165.             'result is:
  166.             'realname with real name of block
  167.             'attlist with att tag and dynprop names
  168.  
  169.  
  170.         End Sub
  171.  
  172. #End Region
  173.  
  174.     End Class
  175. End Namespace

nekitip

  • Guest
Re: inserting dynamic block by name, or reading dyn props (with example)
« Reply #1 on: March 25, 2017, 08:54:13 AM »
after a lot of hair loss this morning in a trial and error attempts, it looks (but if someone would confirm) that the first problem of crashing cad may be solved (not tested enough) by explicitly using collection of dyn props, and then disposing it.

Code - vb.net: [Select]
  1.  If blockDef.IsDynamicBlock Then
  2.  
  3.                         'create new temp ref since i do now know how to get all dynprops
  4.                         'here, something is wrong. could be that there are more anonymous block created and they are not deleted with dispose
  5.                         'how to fix this? Or, how to find dynprop withot creating new ref?
  6.                         'UPDATE: maybe using dpcollection like this, and later dispose it
  7.                         Dim tempDynBlockRef As New BlockReference(New Point3d, blockDef.ObjectId)
  8.                         Dim dpcollection As DynamicBlockReferencePropertyCollection = tempDynBlockRef.DynamicBlockReferencePropertyCollection
  9.  
  10.                         For Each dp As DynamicBlockReferenceProperty In dpcollection
  11.                             If Not dp.ReadOnly Then attList.Add(dp.PropertyName)
  12.  
  13.                         Next
  14.                         dpcollection.Dispose()
  15.                         tempDynBlockRef.Dispose()
  16.  
  17.                     End If

n.yuan

  • Bull Frog
  • Posts: 348
Re: inserting dynamic block by name, or reading dyn props (with example)
« Reply #2 on: March 25, 2017, 09:18:49 AM »
I can see a few issues.

About Inserting a dynamic block (BlockReference), your first CommandMethod:

1. You need to create AttributeReference in the BlockReference first BEFORE apply dynamic properties. That is, the BlockReference is first created as a NORAML BlockReference based on the BlockTableRecord (definition). When dynamic properties are applied to a BlockReference, depending on the nature of dynamic properties, their values..., AutoCAD may create an anonymous BlockTableRecord and the said BlockReference become a reference of the anonymous BlockTableRecord.

In your inserting code, you set dynamic property first, and then add AttributeReference, and you question how to make attribute in right position. While I do not know what the dynamic property does, but I guess it may have something to do with the attribute, thus your code does not place attribute in the place as you expected. So, change the code order to adding attribute first, then set dynamic property after.

2. Your code of setting dynamic property could be problematic: you set its value regardless if the dynamic property is the one expected or not. That is, your code only works if there is only one property, the code would be OK (you do set a variable dpToSearch="xxxx" but never used it. To make code safe, you should test property's name, and only set its value when the property is the one which expects certain value

3. One rarely needs to worry about Transaction.XXXXXGraphicsFlush(). I never needed use them in my AutoCAD .NET API practice, so far.

4. db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES") is used/needed if the block is annotative. That is, if a block is defined as annotative, the block reference should also be annotative

About your second CommandMethod

I am not very sure I know what you want to do here.

Yes, loop through a BlockTableRecord, you can obtain a list of attribute a NORMAL block reference should have. But one should know, attribute definition in block definition only serve as template for attribute reference to be created. When creating AttributeReference, the code has the freedom to actually create the AttributeReference or not. So, having a attribute list from block definition does not guarantee all block references have the same attributes.

As for getting a list of dynamic properties defined for particular block (BlockTableRecord), unfortunately, there is no API exposed to dynamic property definitions. They stored in drawing, somehow linked to the BlockTableRecord as undocumented NamedDictionary data and/or XData and/or ExtensionDictionary.  So, yes, you can only get a dynamic property list via an existing BlockReference by loop through its DynamicBlockReferencePropertyCollection

nekitip

  • Guest
Re: inserting dynamic block by name, or reading dyn props (with example)
« Reply #3 on: March 25, 2017, 12:58:32 PM »
Perfect answer!
Exactly what I needed/was missing/was not sure of.
-so for everyone who googles to here, please read entire anwser from n.yuan, it will help you alot.
(most important parts: when creating, set dynamic properties last (and remember that transformations go from top to bottom of hyararchy), and while reading block by name (and planing to JUST read), temporary create dynamic block reference, take dynprop collection, and dispose both when finished!).

nekitip

  • Guest
Re: inserting dynamic block by name, or reading dyn props (with example)
« Reply #4 on: March 25, 2017, 08:49:35 PM »
...i have a strange feeling that dynprop collection should also be created and disposed on non-temporary object in the same way as in temp object. I've had strange crashes, and I came to suspect that, yet I have not found any source online to confirm this observation...
Other sources online do not mention explicit disposal like here
http://through-the-interface.typepad.com/through_the_interface/2009/03/accessing-the-properties-of-a-dynamic-autocad-block-using-net.html
...however, I do see that they are not iterating directly, but rather also they first create collection (maybe that's all that is needed), however, that's kind of not intuitive. I'm not sure if I can confirm that, since crashes are not following any pattern and are rare, but once I've set .dispose to this location also, they are mostly gone...
I use ac2018 (but app is compiled with .net 4.0)

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: inserting dynamic block by name, or reading dyn props (with example)
« Reply #5 on: April 02, 2017, 12:55:55 AM »
Used this to set dynamic block props while inserting:
Code - C#: [Select]
  1.         private void DoSchedule(Document doc, Database db, Editor ed, Dictionary<string, Dictionary<string, string>> data, string schedBlock)
  2.         {
  3.            try
  4.             {
  5.                 using (Transaction tr = doc.TransactionManager.StartTransaction())
  6.                 {
  7.                     doc.TransactionManager.EnableGraphicsFlush(true);
  8.                     BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  9.                     BlockTableRecord cfgBTR = (BlockTableRecord)tr.GetObject(bt[configBlock], OpenMode.ForRead);
  10.                     ObjectIdCollection cfgBRids = cfgBTR.GetBlockReferenceIds(true, true);
  11.                     BlockReference cfgBR = (BlockReference)tr.GetObject(cfgBRids[0], OpenMode.ForRead);
  12.                     if (!bt.Has(schedBlock))
  13.                     {
  14.                         ed.WriteMessage("\nRequired block \"{0}\" not present in drawing", schedBlock);
  15.                         return;
  16.                     }
  17.                     BlockTableRecord space = (BlockTableRecord)tr.GetObject(cfgBR.OwnerId, OpenMode.ForWrite);
  18.                     List<BlockReference> landmarks = space.getBlocks(landmarkBlock);
  19.                     List<BlockReference> schedItems = space.getBlocks(schedBlock);
  20.                     foreach (BlockReference br in schedItems)
  21.                     {
  22.                         br.UpgradeOpen();
  23.                         br.Erase();
  24.                         br.DowngradeOpen();
  25.                     }
  26.                     foreach (BlockReference br in landmarks)
  27.                     {
  28.                         string landmarkNumber = ((AttributeReference)br.AttributeCollection[0].GetObject(OpenMode.ForRead)).TextString;
  29.                         if (!data.ContainsKey(landmarkNumber)) continue;
  30.                         BlockReference blk = new BlockReference(br.Position, bt[schedBlock]);
  31.                         space.AppendEntity(blk);
  32.                         tr.AddNewlyCreatedDBObject(blk, true);
  33.                         blk.GenerateAttibutes();
  34.                         foreach (ObjectId id in blk.AttributeCollection)
  35.                         {
  36.                             AttributeReference ar = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite);
  37.                             ar.TextString = data[landmarkNumber].ContainsKey(ar.Tag) ?
  38.                                 ar.IsMTextAttribute? data[landmarkNumber][ar.Tag].Replace(((char)10).ToString(),"\\P") :
  39.                                 data[landmarkNumber][ar.Tag].Replace((char)10, (char)32) : "";
  40.                             //ar.TextString = (data.ContainsKey(landmarkNumber) ? (data[landmarkNumber].ContainsKey(ar.Tag) ? data[landmarkNumber][ar.Tag] : ar.Tag) : "");
  41.                             ar.DowngradeOpen();
  42.                         }
  43.                         if (blk.IsDynamicBlock)
  44.                         {
  45.                             //blk.ResetBlock();
  46.                             //doc.TransactionManager.QueueForGraphicsFlush();
  47.                             //doc.TransactionManager.FlushGraphics();
  48.                             Dictionary<string, DynamicBlockReferenceProperty> props = blk.getProperties(false);
  49.                             if (props == null)
  50.                             {
  51.                                 ed.WriteMessage("\nNull property dictionary :(");
  52.                             }
  53.                             if (props.Count == 0)
  54.                             {
  55.                                 ed.WriteMessage("\nEmpty property dictionary :(");
  56.                             }
  57.                             foreach (var prop in props.Keys)
  58.                             {
  59.                                 if (data[landmarkNumber].ContainsKey(prop.ToUpper()))
  60.                                 {
  61.                                     try
  62.                                     {
  63.                                         if (props[prop].UnitsType == DynamicBlockReferencePropertyUnitsType.Distance)
  64.                                             props[prop].Value = double.Parse(data[landmarkNumber][prop.ToUpper()]);
  65.                                         else if (props[prop].PropertyTypeCode == 3)
  66.                                             props[prop].Value = short.Parse(data[landmarkNumber][prop.ToUpper()]);
  67.                                         else
  68.                                             props[prop].Value = data[landmarkNumber][prop.ToUpper()];
  69.                                     }
  70.                                     catch (System.Exception)
  71.                                     {
  72.                                         ed.WriteMessage("\nError setting {0}", prop);
  73.                                     }
  74.                                     //props[prop].UnitsType;
  75.                                 }
  76.                                 else
  77.                                     ed.WriteMessage("\n{0} wasn't in dictionary", prop);
  78.                             }
  79.                             BlockTableRecord btr = (BlockTableRecord)tr.GetObject(blk.DynamicBlockTableRecord, OpenMode.ForRead);
  80.                             btr.UpdateAnonymousBlocks();
  81.                         }
  82.                     }
  83.                     tr.Commit();
  84.                 }
  85.                
  86.             }
  87.             catch (System.Exception ex)
  88.             {
  89.                 Application.ShowAlertDialog(string.Format("{0}", ex));
  90.                 throw;
  91.             }
  92.         }
forgot this dependency:
Code - C#: [Select]
  1.         public static Dictionary<string, DynamicBlockReferenceProperty> getProperties(this BlockReference br, bool IncludeReadOnly)
  2.         {
  3.             if (!br.IsDynamicBlock) return null;
  4.             DynamicBlockReferencePropertyCollection coll = br.DynamicBlockReferencePropertyCollection;
  5.             if (coll.Count == 0) return null;
  6.             Dictionary<string, DynamicBlockReferenceProperty> props = new Dictionary<string,DynamicBlockReferenceProperty>();
  7.             foreach (DynamicBlockReferenceProperty prop in coll)
  8.             {
  9.                 if (IncludeReadOnly | !prop.ReadOnly)
  10.                     props.Add(prop.PropertyName, prop);
  11.             }
  12.             return props;
  13.         }

And:
Code - C#: [Select]
  1.         public static void GenerateAttibutes(this BlockReference br)
  2.         {
  3.             using (Transaction tr = br.Database.TransactionManager.StartTransaction())
  4.             {
  5.                 if (!br.IsWriteEnabled) br.UpgradeOpen();
  6.                 using (BlockTableRecord btr = (BlockTableRecord)br.BlockTableRecord.GetObject(OpenMode.ForRead))
  7.                 {
  8.                     foreach (ObjectId id in btr)
  9.                     {
  10.                         if (id.ObjectClass == RXClass.GetClass(typeof(AttributeDefinition)))
  11.                         {
  12.                             AttributeDefinition ad = (AttributeDefinition)id.GetObject(OpenMode.ForRead);
  13.                             if (ad.Constant) continue;
  14.                             AttributeReference ar = new AttributeReference();
  15.                             ar.SetDatabaseDefaults();
  16.                             ar.SetAttributeFromBlock(ad, br.BlockTransform);
  17.                             br.AttributeCollection.AppendAttribute(ar);
  18.                             tr.AddNewlyCreatedDBObject(ar, true);
  19.                         }
  20.                     }
  21.                 }
  22.                 br.DowngradeOpen();
  23.                 tr.Commit();
  24.             }
  25.         }

AND this lol!
Code - C#: [Select]
  1.         public static List<BlockReference> getBlocks(this BlockTableRecord space, string Name)
  2.         {
  3.             try
  4.             {
  5.                 Database db = space.Database;
  6.                 List<BlockReference> blocks = new List<BlockReference>();
  7.                 ObjectId spId = space.ObjectId;
  8.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  9.                 {
  10.                     BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  11.                     if (!bt.Has(Name)) return null;
  12.                     BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[Name], OpenMode.ForRead);
  13.                     btr.ListBlocks(ref blocks, tr, spId);
  14.                     if (btr.IsDynamicBlock)
  15.                     {
  16.                         foreach (ObjectId id in btr.GetAnonymousBlockIds())
  17.                         {
  18.                             BlockTableRecord abtr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
  19.                             abtr.ListBlocks(ref blocks, tr, spId);
  20.                         }
  21.                     }
  22.  
  23.                     tr.Commit();
  24.                 }
  25.                 return blocks;
  26.             }
  27.             catch (System.Exception ex)
  28.             {
  29.                 throw ex;
  30.             }
  31.         }
« Last Edit: April 03, 2017, 12:41:53 AM by WILL HATCH »