Code Red > .NET
inserting dynamic block by name, or reading dyn props (with example)
nekitip:
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: ---Imports SystemImports Autodesk.AutoCAD.RuntimeImports Autodesk.AutoCAD.ApplicationServicesImports Autodesk.AutoCAD.DatabaseServicesImports Autodesk.AutoCAD.GeometryImports Autodesk.AutoCAD.EditorInput <Assembly: CommandClass(GetType(test1.MyCommands))> Namespace test1 Public Class MyCommands #Region "commands" <CommandMethod("INSERTDYNAMICBLOCK")> Public Shared Sub insertBlock() Dim attList As New List(Of String) Dim name As String = "RMNUM" Dim db As Database = HostApplicationServices.WorkingDatabase() Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.GetDocument(db) Dim ed As Editor = doc.Editor Using trans As Transaction = db.TransactionManager.StartTransaction() ' what is this for? 'doc.TransactionManager.EnableGraphicsFlush(True) Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead) Dim btr As BlockTableRecord = trans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) 'what is this for? 'Dim occ As ObjectContextCollection = db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES") Dim blockDef As BlockTableRecord = trans.GetObject(bt(name), OpenMode.ForRead) 'if bt.has...then Using acNewBlockRef As New BlockReference(New Point3d, blockDef.ObjectId) btr.AppendEntity(acNewBlockRef) trans.AddNewlyCreatedDBObject(acNewBlockRef, True) 'attributes 'classic attrib must always have tag, but tag can be the same 'dynamic prop name must be different'HERE: BLOCK ATTRIBUTES ARE NOT TRANSFORMED WHEN CHANGING DP (or am I missing something simple somewhere) If acNewBlockRef.IsDynamicBlock Then For Each dp As DynamicBlockReferenceProperty In acNewBlockRef.DynamicBlockReferencePropertyCollection If Not dp.ReadOnly Then 'now, IF we change dynprop (flip), then attrib is no longer at proper place without attsync Dim dpnameToSearch As String = "Flip state1" Dim newVal As Double = 1.0 Select Case dp.PropertyTypeCode Case 1 dp.Value = newVal Case 2 dp.Value = Convert.ToInt32(newVal Mod Int32.MaxValue) Case 3 dp.Value = Convert.ToInt16(newVal Mod Int16.MaxValue) Case 4 dp.Value = Convert.ToSByte(newVal Mod Byte.MaxValue) Case 5 dp.Value = Convert.ToString(newVal) Case 13 dp.Value = Convert.ToInt64(newVal Mod Int64.MaxValue) Case Else End Select End If Next End If For Each attId As ObjectId In blockDef Dim ent As Entity = trans.GetObject(attId, OpenMode.ForRead) If TypeOf ent Is AttributeDefinition Then Dim attDef As AttributeDefinition = DirectCast(ent, AttributeDefinition) If (attDef IsNot Nothing) AndAlso (Not attDef.Constant) Then 'This is a non-constant AttributeDefinition Using attRef As New AttributeReference() attRef.SetAttributeFromBlock(attDef, acNewBlockRef.BlockTransform) acNewBlockRef.AttributeCollection.AppendAttribute(attRef) trans.AddNewlyCreatedDBObject(attRef, True) 'CHECK HERE FOR THIS ATTRIB attRef.TextString = "test" If attRef.HasFields Then Dim fOif As ObjectId = attRef.GetField() Dim fo As Field = trans.GetObject(fOif, OpenMode.ForWrite) fo.Evaluate() End If End Using End If End If Next 'what is this for? 'acNewBlockRef.RecordGraphicsModified(True) ' to force updating a block reference End Using 'what is this for? 'trans.TransactionManager.QueueForGraphicsFlush() trans.Commit() End Using End Sub <CommandMethod("GETBLOCKATT")> Public Shared Sub getBlockAttributesByBlockName(g) Dim name As String = "RMNUM" Dim realName As String = "" realName = name Dim attList As New List(Of String) Dim db As Database = HostApplicationServices.WorkingDatabase() Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.GetDocument(db) Dim ed As Editor = doc.Editor Using trans As Transaction = db.TransactionManager.StartTransaction() Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead) If bt.Has(name) Then Dim blockDef As BlockTableRecord = trans.GetObject(bt(name), OpenMode.ForRead) realName = blockDef.Name If blockDef.HasAttributeDefinitions Then For Each chkObjID As ObjectId In blockDef Dim ent As Entity = trans.GetObject(chkObjID, OpenMode.ForRead) If TypeOf ent Is AttributeDefinition Then Dim attDef As AttributeDefinition = DirectCast(ent, AttributeDefinition) attList.Add(attDef.Tag) End If Next End If If blockDef.IsDynamicBlock Then'HERE: CRASH IN 50% TIMES 'create new temp ref since i do now know how to get all dynprops 'here, something is wrong. could be that there are more anonymous block created and they are not deleted with dispose 'how to fix this? Or, how to find dynprop withot creating new ref? Dim tempDynBlockRef As New BlockReference(New Point3d, blockDef.ObjectId) For Each dp As DynamicBlockReferenceProperty In tempDynBlockRef.DynamicBlockReferencePropertyCollection If Not dp.ReadOnly Then attList.Add(dp.PropertyName) Next tempDynBlockRef.Dispose() End If End If trans.Commit() End Using 'result is: 'realname with real name of block 'attlist with att tag and dynprop names End Sub #End Region End ClassEnd Namespace
nekitip:
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: --- If blockDef.IsDynamicBlock Then 'create new temp ref since i do now know how to get all dynprops 'here, something is wrong. could be that there are more anonymous block created and they are not deleted with dispose 'how to fix this? Or, how to find dynprop withot creating new ref? 'UPDATE: maybe using dpcollection like this, and later dispose it Dim tempDynBlockRef As New BlockReference(New Point3d, blockDef.ObjectId) Dim dpcollection As DynamicBlockReferencePropertyCollection = tempDynBlockRef.DynamicBlockReferencePropertyCollection For Each dp As DynamicBlockReferenceProperty In dpcollection If Not dp.ReadOnly Then attList.Add(dp.PropertyName) Next dpcollection.Dispose() tempDynBlockRef.Dispose() End If
n.yuan:
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:
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:
...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)
Navigation
[0] Message Index
[#] Next page
Go to full version