Announcements
Due to scheduled maintenance, the Autodesk Community will be inaccessible from 10:00PM PDT on Oct 16th for approximately 1 hour. We appreciate your patience during this time.
.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

AttSync / Brute Force (VB.Net)

9 REPLIES 9
SOLVED
Reply
Message 1 of 10
JFN_KSH
2285 Views, 9 Replies

AttSync / Brute Force (VB.Net)

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

9 REPLIES 9
Message 2 of 10
_gile
in reply to: JFN_KSH

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

Message 3 of 10
BKSpurgeon
in reply to: JFN_KSH

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

 

 

 

Message 4 of 10
_gile
in reply to: BKSpurgeon

@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

Message 5 of 10
JFN_KSH
in reply to: _gile

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

Message 6 of 10
Paulio
in reply to: _gile

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

Message 7 of 10
Paulio
in reply to: Paulio

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

Message 8 of 10
Paulio
in reply to: Paulio

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
Message 9 of 10
jose.chambel
in reply to: _gile

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é

Message 10 of 10
lcrist
in reply to: JFN_KSH

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

 

BLK.RecordGraphicsModified(True)

 

 

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

Post to forums  

AutoCAD Inside the Factory


Autodesk Design & Make Report