.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to iterate through block values?

11 REPLIES 11
SOLVED
Reply
Message 1 of 12
slecompte
1461 Views, 11 Replies

How to iterate through block values?

How to iterate through a particular named block, type and then update only that have a certain value?

 

I had a request to make a plugin that found every blocked called "TY-I-Generic" that has Type: "GENERIC" with Value: DC and change it to Value:  MC-1.

 

So I adjusted some code I found here:

http://forums.autodesk.com/autodesk/attachments/autodesk/152/16859/1/AttributesVb.txt

 

as this...

 

Public Function SetAttributeValue(ByVal btr As BlockTableRecord, ByVal tag As String, ByVal oldValue As String, ByVal newValue As String) As Integer
            Dim cnt As Integer = 0
            Using tr As Transaction = btr.Database.TransactionManager.StartTransaction
                Dim ids As ObjectIdCollection = btr.GetBlockReferenceIds(True, True)
                If (Not ids Is Nothing) Then
                    Dim btrId As ObjectId
                    For Each btrId In ids
                        Dim blockref As BlockReference = TryCast(btrId.GetObject(OpenMode.ForRead, False), BlockReference)
                        If (Not blockref.AttributeCollection Is Nothing) Then
                            Dim attId As ObjectId
                            For Each attId In blockref.AttributeCollection
                                Dim att As AttributeReference = TryCast(attId.GetObject(OpenMode.ForRead, False), AttributeReference)
                                If String.Equals(att.Tag, tag, StringComparison.OrdinalIgnoreCase) Then

                                    If att.TextString = oldValue Then
                                        Try
                                            att.UpgradeOpen()
                                        Catch ex As Global.Autodesk.AutoCAD.Runtime.Exception
                                            If (ex.ErrorStatus <> ErrorStatus.OnLockedLayer) Then
                                                Throw
                                            End If
                                            Exit For
                                        End Try
                                        att.TextString = newValue
                                        att.DowngradeOpen()
                                        cnt += 1
                                        Exit For
                                    End If
                                End If
                            Next
                        End If
                    Next
                End If
                tr.Commit()
            End Using
            Return cnt
        End Function

 

then a button to trigger it

 

Private Sub btnChange_Click(sender As System.Object, e As System.EventArgs) Handles btnChange.Click

            Dim sOldValue As String
            sOldValue = txtValueFrom.Text
            Dim sNewValue As String
            sNewValue = txtValueTo.Text
            Dim sBlockName As String
            sBlockName = txtBlock.Text

            Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
            Dim id As ObjectId = GetBlockTableRecordId(doc.Database, sBlockName)
            If id.IsNull Then
                doc.Editor.WriteMessage(vbNewLine & "Block {0} not found.", New Object() {sBlockName})
            Else

                Using tr As Transaction = doc.TransactionManager.StartTransaction
                    Dim btr As BlockTableRecord = TryCast(id.GetObject(OpenMode.ForRead), BlockTableRecord)
                    Dim cnt As Integer = SetAttributeValue(btr, Tag, sOldValue, sNewValue)
                    doc.Editor.WriteMessage(vbNewLine & "Updated {0} attributes", New Object() {cnt})
                    tr.Commit()
                End Using
            End If
            MsgBox("Finished changing.")

        End Sub

 

sOldValue = "DC"

sNewValue = "MC-1"

sBlock = "TY-I-GENERIC"

 

are all pulled from text boxes...

 

And if I do SetAttributeValue(btr, Tag, sOldValue, sNewValue) that should do it but no...  Currently with the above I do not get an error but no change either.

 

Also does this need to take into account the Type:  DEVICE?   Do I need to change this code so that it only finds block:  TY-I-GENERIC and its Type:  DEVICE and changes the value MC-1 to TY-I-GENERIC?

 

Is there anything else I can do to change the syntax to make sure this is correct?

 

Thank you in advance.

11 REPLIES 11
Message 2 of 12
norman.yuan
in reply to: slecompte

Did you step through the function SetAttributeValue() in debugging? If you do, you can actually tell how many BlockReferences, and how many Attrubutereferences in each BlockReference the code loops through. If you place a break point at

 

att.TextString=newValue

 

and the break point is hit in debugging, you would have known that the attrbute value did get updated.

 

To me, your code looks fine (as long as the block IS NOT dynamic block). Simply stepping through the code iin debugging would easily tells you why the code works or not works as expected.

 

If the variable "cnt" is 0 after calling SetAttributeValue() and the block definition is a dynamic block, then that is because all the block references have their dynamic property or properties set to values that is different from the value in the block definition, thus these blockreferences cannot be found by BlockTableRecord.GetBlockReferenceIds(). You need to call BlockTableRecord.GetAnonymousBlockIds() and then for each anonymous block Id, you get anonymous BlockTableRecord, then call GetBlockReferenceId() on each anonymous BlockTableRecord.

 

Message 3 of 12
slecompte
in reply to: slecompte

Hi Norman, thank you for your reply.

 

It would be great if I could debug this but not been successful at attempting yet.

 

I've followed these:

 

step-by-step
http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=18171398

video
http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=18172834

 

But when I go to debug I get an error - (Please see Images NotAbleToDebug.png)

The breakpoint will not be hit.

 

Please also note NotAbleTodebug2 - that shows I have the debug spot on according to the video.

 

Any ideas how I may be able to debug this?

 

Message 4 of 12
norman.yuan
in reply to: slecompte

So, your code is executed froom a dialog form (a button clicked, for example)?

 

If so, it is better to better structure your code so that the AutoCAd operation, such as your code shown in the post, from the form's code behind. So you do not hit the FIBERWORLD issue with AutoCAD 2014 oor older version when debugging code with Windows form. This is very well-known issue. You need to set system variable "NEXTFIBERWORLD" to 0 and restart AutoCAD, so that the debugging break point set in the form's code behind can be hit.

 

Just be care, once FIBERWORLD is set to 0, the menu/ribbon would stop working. You need to set it back to 1 after you debugging code on Windows form. Again, structure your code to seperate UI with AutoCAD operation is the best practice to follow, if you can.

Message 5 of 12
slecompte
in reply to: slecompte

So I got the fiber issue with Autodesk worked out and now able to test the coding...

 

When I get to the function SetAttributeValue and set a break point...

 

Dim ids As ObjectIdCollection = btr.GetBlockReferenceIds(True, True)

If (Not ids Is Nothing) Then
            Dim btrId As ObjectId
            For Each btrId In ids

 

...

 

It just goes to Next.

 

It finds the block but does get any blockreferenceIds (please note image provided as attached)

 

 

Message 6 of 12
norman.yuan
in reply to: slecompte

So, if BlockTableRecord.GetBlockReferenceIds() returns a empty ObjectIdCollection, it means there is no BlockReference based on this block definition exists in this drawing; or if the block is dynamic block, then you need to find corresponding anonymous BlockTableRecords, then all the BlockReferences based on these anonymous blocktablerecord (as I mentioned in my previous reply. Is the block a Dynamic block?).

 

Message 7 of 12
slecompte
in reply to: norman.yuan

Yes.  Sorry - just discovered it is a dynamic block.

Message 8 of 12
slecompte
in reply to: slecompte

Thus how do I change the syntax above to identify a dynamic block's attributes and values?

Message 9 of 12
norman.yuan
in reply to: slecompte

I made a some quick change to your SetAttributeValue(), not tested, right after the line "Using tr As Transaction....":

 

    Public Function SetAttributeValue(ByVal btr As BlockTableRecord, _
                                        ByVal tag As String, _
                                        ByVal oldValue As String, _
                                        ByVal newValue As String) As Integer

        Dim cnt As Integer = 0
        Using tr As Transaction = btr.Database.TransactionManager.StartTransaction

            '' get all blockreferences' Id
            Dim brefIds As List(Of ObjectId) = New List(Of ObjectId)

            Dim ids As ObjectIdCollection = btr.GetBlockReferenceIds(True, True)
            For Each id As ObjectId In ids
                brefIds.add(id)
            Next

            '' get all dynamic blockreferences
            If btr.IsDynamicBlock Then
                Dim brIds As ObjectIdCollection = btr.GetAnonymousBlockIds()
                For Each id As ObjectId In brIds
                    Dim br As BlockTableRecord = _
                        DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockTableRecord)
                    ids = br.GetBlockReferenceIds(True, True)
                    For Each id As ObjectId In ids
                        brefIds.Add(id)
                    Next
                Next
            End If

            If (brefIds.Count > 0) Then

                For Each btrId As ObjectId In brefIds

                    Dim blockref As BlockReference = _
                        TryCast(btrId.GetObject(OpenMode.ForRead, False), BlockReference)

                    If (Not blockref.AttributeCollection Is Nothing) Then
                        Dim attId As ObjectId
                        For Each attId In blockref.AttributeCollection
                            Dim att As AttributeReference = _
                                TryCast(attId.GetObject(OpenMode.ForRead, False), AttributeReference)
                            If String.Equals(att.Tag, tag, StringComparison.OrdinalIgnoreCase) Then

                                If att.TextString = oldValue Then
                                    Try
                                        att.UpgradeOpen()
                                    Catch ex As Global.Autodesk.AutoCAD.Runtime.Exception
                                        If (ex.ErrorStatus <> ErrorStatus.OnLockedLayer) Then
                                            Throw
                                        End If
                                        Exit For
                                    End Try
                                    att.TextString = newValue
                                    att.DowngradeOpen()
                                    cnt += 1
                                    Exit For
                                End If
                            End If
                        Next
                    End If
                Next

            End If

            tr.Commit()

        End Using
        Return cnt
    End Function

 

Message 10 of 12
slecompte
in reply to: slecompte

I got one last error. 

 

Variable id hides a variable in an enclosing block.

 

Please note attachment:

 

I do not understand why it doesn't show for the first instance of its use....4 lines above it.

Message 11 of 12
norman.yuan
in reply to: slecompte

Change this:

 

'' get all dynamic blockreferences
            If btr.IsDynamicBlock Then
                Dim brIds As ObjectIdCollection = btr.GetAnonymousBlockIds()
                For Each id As ObjectId In brIds
                    Dim br As BlockTableRecord = _
                        DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockTableRecord)
                    ids = br.GetBlockReferenceIds(True, True)
                    For Each id As ObjectId In ids
                        brefIds.Add(id)
                    Next
                Next
            End If

 

To This:

 

'' get all dynamic blockreferences
            If btr.IsDynamicBlock Then
                Dim brIds As ObjectIdCollection = btr.GetAnonymousBlockIds()
                For Each id As ObjectId In brIds
                    Dim br As BlockTableRecord = _
                        DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockTableRecord)
                    ids = br.GetBlockReferenceIds(True, True)
                    For Each idd As ObjectId In ids
                        brefIds.Add(idd)
                    Next
                Next
            End If

Message 12 of 12
slecompte
in reply to: norman.yuan

That worked great.  Thank you sincerely for helping me with this.

It is much appreciated.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost