AttSync / Brute Force (VB.Net)

JFN_KSH
Advocate
Advocate

AttSync / Brute Force (VB.Net)

JFN_KSH
Advocate
Advocate

Hi,

I`ve written some code that changes the layer of selected blocks & nested blocks. Blockreference(s) containing attributes needs to be synced (attsync). What`s the best way of doing an attsync in .Net? I`ve look at the following Brute force method from Gile (NET BLOCK Routines), but I`m getting some intellisense error:

 

'GetAttributes' is not a member of 'BlockTableRecord'

'ResetAttributes' is not a member of 'BlockReference'

 

My target framework in set to 4.5

My AcCoreMgd, AcDbMgd & AcDbMgd are on version 20.1.0.0

 

Any help is appreciated!

Capture.JPG

0 Likes
Reply
Accepted solutions (1)
2,402 Views
9 Replies
Replies (9)

_gile
Mentor
Mentor
Accepted solution

Hi,

 

It looks like you didn't copy the whole code from TheSwamp: the GetAttributes() and ResetAttributes() methods are missing in the code you show.

 

With VB, you have to clear the Root Namespace in the project to add these extension methods to Autodesk.AutoCAD.DatabaseServices.

 

 

    Imports Autodesk.AutoCAD.DatabaseServices
    Imports Autodesk.AutoCAD.Runtime
     
    Namespace Autodesk.AutoCAD.DatabaseServices
        Public Module ExtensionMethods
     
            Dim attDefClass As RXClass = RXClass.GetClass(GetType(AttributeDefinition))
     
            <System.Runtime.CompilerServices.Extension> _
            Public Sub SynchronizeAttributes(target As BlockTableRecord)
                If target Is Nothing Then
                    Throw New ArgumentNullException("target")
                End If
     
                Dim tr As Transaction = target.Database.TransactionManager.TopTransaction
                If tr Is Nothing Then
                    Throw New Exception(ErrorStatus.NoActiveTransactions)
                End If
     
                Dim attDefs As List(Of AttributeDefinition) = target.GetAttributes(tr)
                For Each id As ObjectId In target.GetBlockReferenceIds(True, False)
                    Dim br As BlockReference = _
                        DirectCast(tr.GetObject(id, OpenMode.ForWrite), BlockReference)
                    br.ResetAttributes(attDefs, tr)
                Next
                If target.IsDynamicBlock Then
                    target.UpdateAnonymousBlocks()
                    For Each id As ObjectId In target.GetAnonymousBlockIds()
                        Dim btr As BlockTableRecord = _
                            DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockTableRecord)
                        attDefs = btr.GetAttributes(tr)
                        For Each brId As ObjectId In btr.GetBlockReferenceIds(True, False)
                            Dim br As BlockReference = _
                                DirectCast(tr.GetObject(brId, OpenMode.ForWrite), BlockReference)
                            br.ResetAttributes(attDefs, tr)
                        Next
                    Next
                End If
            End Sub
     
            <System.Runtime.CompilerServices.Extension> _
            Private Function GetAttributes(target As BlockTableRecord, tr As Transaction) As List(Of AttributeDefinition)
                Dim attdefs As List(Of AttributeDefinition) = New List(Of AttributeDefinition)
                For Each id As ObjectId In target
                    If id.ObjectClass = attDefClass Then
                        Dim attDef As AttributeDefinition = _
                            DirectCast(tr.GetObject(id, OpenMode.ForRead), AttributeDefinition)
                        attdefs.Add(attDef)
                    End If
                Next
                Return attdefs
            End Function
     
            <System.Runtime.CompilerServices.Extension> _
            Private Sub ResetAttributes(br As BlockReference, attDefs As List(Of AttributeDefinition), tr As Transaction)
                Dim attValues As New Dictionary(Of String, String)()
                For Each id As ObjectId In br.AttributeCollection
                    If Not id.IsErased Then
                        Dim attRef As AttributeReference = _
                            DirectCast(tr.GetObject(id, OpenMode.ForWrite), AttributeReference)
                        attValues.Add( _
                            attRef.Tag, _
                            If(attRef.IsMTextAttribute, attRef.MTextAttribute.Contents, attRef.TextString))
                        attRef.Erase()
                    End If
                Next
                For Each attDef As AttributeDefinition In attDefs
                    Dim attRef As New AttributeReference()
                    attRef.SetAttributeFromBlock(attDef, br.BlockTransform)
                    If attDef.Constant Then
                        attRef.TextString = If(attDef.IsMTextAttributeDefinition, _
                                               attDef.MTextAttributeDefinition.Contents, _
                                               attDef.TextString)
                    Else If attValues IsNot Nothing AndAlso attValues.ContainsKey(attDef.Tag) Then
                        attRef.TextString = attValues(attDef.Tag.ToUpper())
                    End If
                    br.AttributeCollection.AppendAttribute(attRef)
                    tr.AddNewlyCreatedDBObject(attRef, True)
                Next
            End Sub
     
        End Module
     
    End Namespace

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

BKSpurgeon
Collaborator
Collaborator

Looks like you are missing some extension method(s)

 

an internet search pulled up with these results: 

 

https://www.theswamp.org/index.php?topic=42591.0

 

 

it's in c# i'm sure you'd be able to find the exact ones in VB.NET with a good google search.

 

best rgds

 

 

 

0 Likes

_gile
Mentor
Mentor

@BKSpurgeon, the whole code (C# and VB) is in the message linked by @JFN_KSH:

http://www.theswamp.org/index.php?topic=31859.msg508802#msg508802



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

JFN_KSH
Advocate
Advocate

Thanks Gile, it's all good now!! This code is genius! You got my admiration... I don't know why I removed theses lines of code... Thanks again.Smiley Wink

0 Likes

Paulio
Advocate
Advocate

Hi Gile, 

 

Firstly let me say thank you for sharing your code, it's helped me enormously. 

I have discovered a minor problem.

Steps to reproduce:

  • Rotate an attributed block in the drawing
  • Run the attsync code
  • Go to the properties of the block
  • Enter 0 in the Rotation

AutoCAD then displays a message: Error, Object was erased. The block's rotation is set to 0 but the attributes remain where they were before the rotation back to 0.

Capture.PNG

I was wondering if you could confirm whether you see the same behaviour and if not, do you have any suggestions as to what might be causing the problem?

 

This is how I'm calling it:

''' <summary>
''' Synchronise block reference attributes with their definitions
''' </summary>
''' <param name="blkRefIds">The objectIds of the block references to synchronise</param>
Public Shared Sub myAttSync(blkRefIds As List(Of ObjectId))
    Dim db As Database = HostApplicationServices.WorkingDatabase
    Dim doc As Document = Application.DocumentManager.GetDocument(db)
    Using docLock As DocumentLock = doc.LockDocument()
        Using trans As Transaction = db.TransactionManager.StartTransaction()
' If no specific blocks supplied then synchronise all...             If blkRefIds Is Nothing Then                 Dim bt As BlockTable = trans.GetObject(db.BlockTableId, OpenMode.ForRead)                 blkRefIds = bt.Cast(Of ObjectId)().ToList()             End If             For Each id As ObjectId In blkRefIds                 Dim btr As BlockTableRecord = trans.GetObject(id, OpenMode.ForRead)                 If btr.IsLayout Then                     Continue For                 End If                 If btr.IsFromExternalReference Then                     Continue For                 End If                 If btr.GetBlockReferenceIds(TrueTrue).Count = 0 Then                     Continue For                 End If                 btr.UpgradeOpen()                 btr.SynchronizeAttributes()             Next             trans.Commit()         End Using     End Using End Sub

Thanks

 

Paul

0 Likes

Paulio
Advocate
Advocate

I think I've fixed it.

 

I've changed ResetAttributes to look like this so rather than deleting each one and recreating it, it simply updates them from the definition:

    <System.Runtime.CompilerServices.Extension>
    Private Sub ResetAttributes(br As BlockReference, attdefs As List(Of AttributeDefinition), trans As Transaction)
        Dim attValues As New Dictionary(Of String, String)()
        For Each id As ObjectId In br.AttributeCollection
            If Not id.IsErased Then
                Dim attRef As AttributeReference = DirectCast(trans.GetObject(id, OpenMode.ForWrite), AttributeReference)
                Dim attDef As AttributeDefinition = attdefs.Find(Function(x) x.Tag = attRef.Tag)
                If attDef Is Nothing Then
                    attRef.Erase()
                    Continue For
                End If
                Dim attText As String = If(attRef.IsMTextAttribute, attRef.MTextAttribute.Contents, attRef.TextString)
                attRef.SetAttributeFromBlock(attDef, br.BlockTransform)
                attRef.TextString = attText
                attdefs.Remove(attDef)
            End If
        Next
        For Each attDef As AttributeDefinition In attdefs
            Dim attRef As New AttributeReference()
            attRef.SetAttributeFromBlock(attDef, br.BlockTransform)
            If attDef.Constant Then
                attRef.TextString = If(attDef.IsMTextAttributeDefinition, attDef.MTextAttributeDefinition.Contents, attDef.TextString)
            ElseIf attValues IsNot Nothing AndAlso attValues.ContainsKey(attDef.Tag) Then
                attRef.TextString = attValues(attDef.Tag.ToUpper())
            End If
            br.AttributeCollection.AppendAttribute(attRef)
            trans.AddNewlyCreatedDBObject(attRef, True)
        Next
    End Sub

This also handles blocks that have duplicate attributes. I know it doesn't make sense for attributes to be duplicated but AutoCAD allows users to create duplicates, and in my experience if they can do it, they will!

Can you see any flaws in the above?

 

Thanks again

 

Paul

0 Likes

Paulio
Advocate
Advocate

Update:

There was an issue with the code I posted. It would only work for the first instance of a particular block. The second one would get its attributes removed. It now looks like this:

<System.Runtime.CompilerServices.Extension>
    Private Sub ResetAttributes(br As BlockReference, attdefs As List(Of AttributeDefinition), trans As Transaction)
Dim defsCopy As New List(Of AttributeDefinition)(attdefs)
        Dim attValues As New Dictionary(Of String, String)()
        For Each id As ObjectId In br.AttributeCollection
            If Not id.IsErased Then
                Dim attRef As AttributeReference = DirectCast(trans.GetObject(id, OpenMode.ForWrite), AttributeReference)
                Dim attDef As AttributeDefinition = attdefs.Find(Function(x) x.Tag = attRef.Tag)
                If attDef Is Nothing Then
                    attRef.Erase()
                    Continue For
                End If
                Dim attText As String = If(attRef.IsMTextAttribute, attRef.MTextAttribute.Contents, attRef.TextString)
                attRef.SetAttributeFromBlock(attDef, br.BlockTransform)
                attRef.TextString = attText
                defsCopy.Remove(attDef)
            End If
        Next
        For Each attDef As AttributeDefinition In defsCopy
            Dim attRef As New AttributeReference()
            attRef.SetAttributeFromBlock(attDef, br.BlockTransform)
            If attDef.Constant Then
                attRef.TextString = If(attDef.IsMTextAttributeDefinition, attDef.MTextAttributeDefinition.Contents, attDef.TextString)
            ElseIf attValues IsNot Nothing AndAlso attValues.ContainsKey(attDef.Tag) Then
                attRef.TextString = attValues(attDef.Tag.ToUpper())
            End If
            br.AttributeCollection.AppendAttribute(attRef)
            trans.AddNewlyCreatedDBObject(attRef, True)
        Next
    End Sub
0 Likes

jose.chambel
Explorer
Explorer

Hello Giles. I'm trying to apply your Attsync solution, but I can not make it work in multithreading or Backgroundworker. It's not possible? I know that Autocad has a bad behavior with multithreading. Is this the cause? When I run it as a simple method, everything works fine.

 

Thank's

José

0 Likes

lcrist
Explorer
Explorer

How about this, per BlockReference? Assuming BLK As BlockReference, then

 

BLK.RecordGraphicsModified(True)

 

 

0 Likes