Code Red > .NET

inserting dynamic block by name, or reading dyn props (with example)

(1/2) > >>

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