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

ATTSYNC in VB.NET

23 REPLIES 23
SOLVED
Reply
Message 1 of 24
mindofcat
10160 Views, 23 Replies

ATTSYNC in VB.NET

Hi all,

 

Does anyone know what the dot net equivalent is for attsync?

 

I use invisible attributes in the block references created by my dot net code, but I just noticed a strange AutoCAD behaviour:

 

Right-clicking on the block with attributes, and selecting the 'Edit Block in-place' option correctly displays the hidden attributes, making it possible for them to be deleted.

 

However, after deleting all the attributes and saving the ref edit, I right-click on the same block again, this time to check if it still has attibutes; and it does, even though I deleted all the attributes! There seems to be some sort of 'held-in-memory' issue here, which, after I did some research, I discovered that the ATTSYNC command is perfect for purging those deleted attributes from the block reference, AFTER they have been physically deleted.

 

The problem is, how do I implement this ATTSYNC command in my dot net code, without using sendcommand? 

 

I found a thread on this forum regarding this issue, which led me to the swamp; but then the code I found there is in F#, and so I was wondering if anyone knows of a simple VB.NET way of mimicking the AutoCAD ATTSYNC command?

 

Many thanks...

 

Cat

23 REPLIES 23
Message 2 of 24
_gile
in reply to: mindofcat

Hi,

 

Here's a simple and 'brute force' method which erases the existing AttributeReferences from the inserted BlockReferences and adds them new ones from the BlockTableRecord AttributeDefinitions.

 

The SynchronizeAttributes() method is defined as an extension method for the BlockTableRecord type so that it can be called as an instance method of this type.

 

Example (assuming btr is a BlocTableRecord instance) : btr.SynchronizeAttributes()

 

C# code

using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using AcRx = Autodesk.AutoCAD.Runtime;

namespace AttSyncSample
{
    public static class ExtensionMethods
    {
        public static void SynchronizeAttributes(this BlockTableRecord target)
        {
            if (target == null)
                throw new ArgumentNullException("btr");

            Transaction tr = target.Database.TransactionManager.TopTransaction;
            if (tr == null)
                throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);


            RXClass attDefClass = RXClass.GetClass(typeof(AttributeDefinition));
            List<AttributeDefinition> attDefs = new List<AttributeDefinition>();
            foreach (ObjectId id in target)
            {
                if (id.ObjectClass == attDefClass)
                {
                    AttributeDefinition attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
                    attDefs.Add(attDef);
                }
            }
            foreach (ObjectId id in target.GetBlockReferenceIds(true, false))
            {
                BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
                br.ResetAttributes(attDefs);
            }
            if (target.IsDynamicBlock)
            {
                foreach (ObjectId id in target.GetAnonymousBlockIds())
                {
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                    foreach (ObjectId brId in btr.GetBlockReferenceIds(true, false))
                    {
                        BlockReference br = (BlockReference)tr.GetObject(brId, OpenMode.ForWrite);
                        br.ResetAttributes(attDefs);
                    }
                }
            }
        }

        private static void ResetAttributes(this BlockReference br, List<AttributeDefinition> attDefs)
        {
            TransactionManager tm = br.Database.TransactionManager;
            Dictionary<string, string> attValues = new Dictionary<string, string>();
            foreach (ObjectId id in br.AttributeCollection)
            {
                if (!id.IsErased)
                {
                    AttributeReference attRef = (AttributeReference)tm.GetObject(id, OpenMode.ForWrite);
                    attValues.Add(attRef.Tag, attRef.TextString);
                    attRef.Erase();
                }
            }
            foreach (AttributeDefinition attDef in attDefs)
            {
                AttributeReference attRef = new AttributeReference();
                attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
                if (attValues.ContainsKey(attDef.Tag))
                {
                    attRef.TextString = attValues[attDef.Tag.ToUpper()];
                }
                br.AttributeCollection.AppendAttribute(attRef);
                tm.AddNewlyCreatedDBObject(attRef, true);
            }
        }
    }
}

 

VB code

Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Runtime

Module ExtensionMethods

    <System.Runtime.CompilerServices.Extension> _
    Public Sub SynchronizeAttributes(target As BlockTableRecord)
        If target Is Nothing Then
            Throw New ArgumentNullException("btr")
        End If

        Dim tr As Transaction = target.Database.TransactionManager.TopTransaction
        If tr Is Nothing Then
            Throw New Exception(ErrorStatus.NoActiveTransactions)
        End If

        Dim attDefClass As RXClass = RXClass.GetClass(GetType(AttributeDefinition))
        Dim attDefs As 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
        For Each id As ObjectId In target.GetBlockReferenceIds(True, False)
            Dim br As BlockReference = DirectCast(tr.GetObject(id, OpenMode.ForWrite), BlockReference)
            br.ResetAttributes(attDefs)
        Next
        If target.IsDynamicBlock Then
            For Each id As ObjectId In target.GetAnonymousBlockIds()
                Dim btr As BlockTableRecord = DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockTableRecord)
                For Each brId As ObjectId In btr.GetBlockReferenceIds(True, False)
                    Dim br As BlockReference = DirectCast(tr.GetObject(brId, OpenMode.ForWrite), BlockReference)
                    br.ResetAttributes(attDefs)
                Next
            Next
        End If
    End Sub

    <System.Runtime.CompilerServices.Extension> _
    Private Sub ResetAttributes(br As BlockReference, attDefs As List(Of AttributeDefinition))
        Dim tm As TransactionManager = br.Database.TransactionManager
        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(tm.GetObject(id, OpenMode.ForWrite), AttributeReference)
                attValues.Add(attRef.Tag, 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 attValues IsNot Nothing AndAlso attValues.ContainsKey(attDef.Tag) Then
                attRef.TextString = attValues(attDef.Tag.ToUpper())
            End If
            br.AttributeCollection.AppendAttribute(attRef)
            tm.AddNewlyCreatedDBObject(attRef, True)
        Next
    End Sub

End Module

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 24
mindofcat
in reply to: _gile

Many thanks, Gilles... your code worked the magic!

Message 4 of 24
_gile
in reply to: mindofcat

You're welcome.

 

if you want it to work with mtext attributes, in the ResetAttributes method, replace:

 

attValues.Add(attRef.Tag, attRef.TextString)

with,

C#

attValues.Add(attRef.Tag,
    attRef.IsMTextAttribute ? attRef.MTextAttribute.Contents : attRef.TextString);

 

VB

attValues.Add( _
    attRef.Tag, _
    If(attRef.IsMTextAttribute, attRef.MTextAttribute.Contents, attRef.TextString))

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 5 of 24
_gile
in reply to: _gile

You can find a better implementation (works with multiline attributes, constant attributes, dynamic blocks) at TheSwamp:

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



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 24
mindofcat
in reply to: _gile

Quite right it is, worked like a charm! many thanks again!
Message 7 of 24
arcticad
in reply to: _gile

 

 

---------------------------



(defun botsbuildbots() (botsbuildbots))
Message 8 of 24
shih
in reply to: arcticad

Trying the following codes to open a drawing in memory but above codes seems doesn't works using (Database db = new Database(false, true)) { try { db.ReadDwgFile(theProjDwg4CNBlkReplace, System.IO.FileShare.ReadWrite, false, null); using (Transaction trans = db.TransactionManager.StartTransaction()) { } } catch {} }
Message 9 of 24
shih
in reply to: shih

I found the solution from the following link.

 

http://through-the-interface.typepad.com/through_the_interface/2007/07/updating-a-sp-1.html?cid=6a00...

 

Update 2:

The above code does not realign attributes after editing their values: if your attributes are anything other than left-justified, you will need to make a call to AdjustAlignment on the attribute reference after editing it.

There's a trick to this: you need to make sure the working database is set to the drawing you're working on, as well as passing it as an argument to the function.

You could set the working database early in the code, or insert this code to do it locally (the choice is yours):

  ar.TextString = attbValue;

 

  // Begin alignment code

 

  Database wdb = HostApplicationServices.WorkingDatabase;

  HostApplicationServices.WorkingDatabase = db;

  ar.AdjustAlignment(db);

  HostApplicationServices.WorkingDatabase = wdb;

 

  // End alignment code

 

  ar.DowngradeOpen();

 

 

Message 10 of 24
stefan.hofer
in reply to: _gile

This code work perfect in AutoCAD.

But it does nothing in AutoCAD Mechanical... any idea why?

 

i have also tried ed.Command("_.attsync", "_name", bname);

this also don't work in mechanical. but if i write the command manually in the command line, it works.

Message 11 of 24
stefan.hofer
in reply to: stefan.hofer

If someone have the same problem, this code works for me:

AcadDocument thisDrawing = (AcadDocument)acApp.DocumentManager.MdiActiveDocument.GetAcadDocument();
thisDrawing.SendCommand("_.attsync\n" + "_name\n" + bname + "\n");
Message 12 of 24
lvthunder
in reply to: _gile

I'm using VB.NET and when I add this code I get errors in other parts of my program "Autodesk.AutoCAD.ApplicationServices" is not a member of MyProgram.Autodesk.AutoCAD.  How can I fix this?

 

I'm trying to do this because I want to insert blocks with attributes in a drawing that are a different scale than the drawing I'm inserting them into.  The attributes don't scale with the block.  If I run attsync on the drawing when it's done they show up where they need to be.

Message 13 of 24
_gile
in reply to: lvthunder

Hi,

 

I do not know what is: "MyProgram.Autodesk.AutoCAD". You should provide more details on how you're trying to use this code, or simply show the code you use to insert blocks. If it's a simple question of scale it may be solved while you're inserting the attribute definitions.

 

Another thing, I do not use VB but I often saw people using VB who have problem when using extension methods  (may be one more crappy VB behavior).  You can also use these methods as classical static (Shared) methods.

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 14 of 24
lvthunder
in reply to: _gile

Yeah, this is an older program that I wrote years ago, but I need to update it for our new project.  I wouldn't mind updating it to c#, but I don't know how to go about it without rewriting it from scratch.

 

Here is my code to insert the block

    Public Sub InsertDrawingAsBlockExplode(ByVal doc As Document, ByVal filepath As String, ByVal blockname As String, ByVal ipt As Point3d, ByVal rotate As String, ByVal scale As String)
        If My.Computer.FileSystem.FileExists(filepath) Then
            Dim curdb As Database = doc.Database
            Dim ed As Editor = doc.Editor
            Dim loc As DocumentLock = doc.LockDocument()
            Using loc
                Dim tr As Transaction = doc.TransactionManager.StartTransaction
                Using tr
                    Try
                        Dim blkid As ObjectId
                        Dim bt As BlockTable = CType(tr.GetObject(curdb.BlockTableId, OpenMode.ForRead, False), BlockTable)
                        If bt.Has(blockname) Then
                            Do Until bt.Has(blockname) = False
                                blockname = blockname & 1
                            Loop
                            'Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(String.Format("Block ""{0}"" does already exist" & vbLf & "Try another name", blockname))
                            'Return Nothing
                        End If
                        Dim btr As BlockTableRecord = CType(tr.GetObject(curdb.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord)
                        Dim db As Database = New Database(False, True)
                        Using db
                            db.ReadDwgFile(filepath, System.IO.FileShare.Read, False, "")
                            blkid = curdb.Insert(blockname, db, True)
                        End Using
                        If (Not bt.Has(blockname)) Then
                            Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(String.Format("Block ""{0}"" does not added to block table" & vbLf & "Exit on Error", blockname))
                            Return
                        End If
                        Dim btrec As BlockTableRecord = CType(tr.GetObject(blkid, OpenMode.ForRead, False), BlockTableRecord)
                        If String.IsNullOrEmpty(btrec.Name) Then
                            btrec.UpgradeOpen()
                            btrec.Name = blockname
                            btrec.DowngradeOpen()
                        End If
                        Dim bref As BlockReference = New BlockReference(ipt, blkid)
                        Dim mat As Matrix3d = Matrix3d.Identity
                        bref.TransformBy(mat)
                        bref.ScaleFactors = New Scale3d(scale)
                        bref.Rotation = rotate
                        btr.AppendEntity(bref)
                        tr.AddNewlyCreatedDBObject(bref, True)
                        bref.ExplodeToOwnerSpace()
                        bref.Erase()
                        If btrec.HasAttributeDefinitions Then
                            Dim atcoll As Autodesk.AutoCAD.DatabaseServices.AttributeCollection = bref.AttributeCollection
                            For Each subid As ObjectId In btrec
                                Dim ent As Entity = DirectCast(subid.GetObject(OpenMode.ForRead), Entity)
                                Dim attDef As AttributeDefinition = TryCast(ent, AttributeDefinition)
                                If attDef IsNot Nothing Then
                                    ed.WriteMessage(vbLf & "Value: " + attDef.TextString)
                                    Dim attRef As New AttributeReference()
                                    attRef.SetPropertiesFrom(attDef)
                                    attRef.Visible = attDef.Visible
                                    attRef.SetAttributeFromBlock(attDef, bref.BlockTransform)
                                    attRef.HorizontalMode = attDef.HorizontalMode
                                    attRef.VerticalMode = attDef.VerticalMode
                                    attRef.Rotation = attDef.Rotation
                                    attRef.TextStyleId = attDef.TextStyleId
                                    attRef.Position = attDef.Position + ipt.GetAsVector()
                                    attRef.Tag = attDef.Tag
                                    attRef.FieldLength = attDef.FieldLength
                                    attRef.TextString = attDef.TextString
                                    attRef.AdjustAlignment(curdb)
                                    atcoll.AppendAttribute(attRef)
                                    tr.AddNewlyCreatedDBObject(attRef, True)
                                End If
                            Next
                        End If
                        'ed.UpdateScreen()
                        tr.Commit()
                    Catch ex As Autodesk.AutoCAD.Runtime.Exception
                        Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(ex.ToString & vbCr & ex.Message)
                    End Try
                End Using
            End Using
        Else
            MsgBox(filepath & " does not exist!")
        End If
    End Sub

 

Then here is how it updates the attributes.

    Sub UpdateAttributeValues(ByVal BlockID As ObjectId, ByVal AttributeTagsValues As Dictionary(Of String, String))
        Using myTrans As Transaction = BlockID.Database.TransactionManager.StartTransaction
            Dim myBref As BlockReference = BlockID.GetObject(OpenMode.ForRead)
            Dim myAttCollection As AttributeCollection = myBref.AttributeCollection
            For Each myattrefid As ObjectId In myAttCollection
                Dim myAttRef As AttributeReference = myattrefid.GetObject(OpenMode.ForWrite)
                If AttributeTagsValues.ContainsKey(myAttRef.Tag) Then
                    myAttRef.TextString = AttributeTagsValues(myAttRef.Tag)
                    myAttRef.AdjustAlignment(BlockID.Database)
                End If
            Next
            myTrans.Commit()
        End Using
    End Sub
Message 15 of 24
lvthunder
in reply to: lvthunder

Here is a screenshot showing the error it's giving me.

Autodesk error.jpg

Message 16 of 24
_gile
in reply to: lvthunder

I do not know what is 'ShopDrawings', but it seems to be a namespace issue.

I know VB makes the namespaces stuff unclear by implicitly creating a root namespace with the project name (read this topic).



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 17 of 24
lvthunder
in reply to: _gile

Yes, ShopDrawings is the name of my program, and from what I read the default namespace.  I think I'll try making them regular methods and see if that works.  Did you look through the code I'm using to insert the block and see if there was something I could do there to fix it on insert instead of having to do this?

Message 18 of 24
brian.k.smith
in reply to: _gile

This worked for me!   Merci, @_gile 

Message 19 of 24
CBennett34F8SS
in reply to: mindofcat

This works great, but when I try to access the attribute collection after the SynchronizeAttributes has been committed. I get ewaserased.  If I try to access it before the transaction is commited I get the old data.  Does anyone know how to get the new instance of the attribute collection?

Message 20 of 24
_gile
in reply to: CBennett34F8SS

Hi,

If you want someone to be able to help you, you should show your code.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

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