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
Solved! Go to Solution.
Solved by _gile. Go to Solution.
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
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))
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
I found the solution from the following link.
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();
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.
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");
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.
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.
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
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).
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?
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?
Can't find what you're looking for? Ask the community or share your knowledge.