Author Topic: Synchronize attributes (ATTSYNC)  (Read 1370 times)

0 Members and 1 Guest are viewing this topic.

Offline (gile)

  • C#
  • *
  • Posts: 87
  • Karma: +8/-0
  • Gender: Male
    • prefered language: F
    • Prog expertise: Good
    • View Profile
Synchronize attributes (ATTSYNC)
« on: November 23, 2013, 05:43:42 PM »
Hi,

This is a simple and 'brute force' method to mimic the ATTSYNC command. It 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.
Using example (assuming btr is a BlocTableRecord instance) : btr.SynchronizeAttributes()

<EDIT: works now w/ mtext attributes>
<EDIT: corrected an issue w/ attributes in dynamic blocks>
<EDIT: corrected an issue w/ constant attributes>

 C# code
Code: [Select]
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using AcRx = Autodesk.AutoCAD.Runtime;

namespace Autodesk.AutoCAD.DatabaseServices
{
    public static class ExtensionMethods
    {
        static RXClass attDefClass = RXClass.GetClass(typeof(AttributeDefinition));

        public static void SynchronizeAttributes(this BlockTableRecord target)
        {
            if (target == null)
                throw new ArgumentNullException("target");

            Transaction tr = target.Database.TransactionManager.TopTransaction;
            if (tr == null)
                throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
            List<AttributeDefinition> attDefs = target.GetAttributes(tr);
            foreach (ObjectId id in target.GetBlockReferenceIds(true, false))
            {
                BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
                br.ResetAttributes(attDefs, tr);
            }
            if (target.IsDynamicBlock)
            {
                target.UpdateAnonymousBlocks();
                foreach (ObjectId id in target.GetAnonymousBlockIds())
                {
                    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                    attDefs = btr.GetAttributes(tr);
                    foreach (ObjectId brId in btr.GetBlockReferenceIds(true, false))
                    {
                        BlockReference br = (BlockReference)tr.GetObject(brId, OpenMode.ForWrite);
                        br.ResetAttributes(attDefs, tr);
                    }
                }
            }
        }

        private static List<AttributeDefinition> GetAttributes(this BlockTableRecord target, Transaction tr)
        {
            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);
                }
            }
            return attDefs;
        }

        private static void ResetAttributes(this BlockReference br, List<AttributeDefinition> attDefs, Transaction tr)
        {
            Dictionary<string, string> attValues = new Dictionary<string, string>();
            foreach (ObjectId id in br.AttributeCollection)
            {
                if (!id.IsErased)
                {
                    AttributeReference attRef = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite);
                    attValues.Add(attRef.Tag,
                        attRef.IsMTextAttribute ? attRef.MTextAttribute.Contents : attRef.TextString);
                    attRef.Erase();
                }
            }
            foreach (AttributeDefinition attDef in attDefs)
            {
                AttributeReference attRef = new AttributeReference();
                attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
                if (attDef.Constant)
                {
                    attRef.TextString = attDef.IsMTextAttributeDefinition ?
                        attDef.MTextAttributeDefinition.Contents :
                        attDef.TextString;
                }
                else if (attValues.ContainsKey(attRef.Tag))
                {
                    attRef.TextString = attValues[attRef.Tag];
                }
                br.AttributeCollection.AppendAttribute(attRef);
                tr.AddNewlyCreatedDBObject(attRef, true);
            }
        }
    }
}

VB code (have to clear the Root Namespace in the project to add these extension methods to Autodesk.AutoCAD.DatabaseServices)
Code: [Select]
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
« Last Edit: November 26, 2013, 02:50:13 PM by (gile) »