Author Topic: .NET BLOCK Routines  (Read 66132 times)

0 Members and 1 Guest are viewing this topic.

Galway

  • Guest
Re: .NET BLOCK Routines
« Reply #30 on: November 24, 2014, 07:55:36 PM »
Hi,

This is a simple and 'brute force' method to mimic the ATTSYNC command. It replaces the existing AttributeReferences from the inserted BlockReferences with 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: corrected an issue w/ attributes in dynamic blocks>
<EDIT: corrected an issue w/ constant attributes>

 C# code
Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Runtime;
  5. using AcRx = Autodesk.AutoCAD.Runtime;
  6.  
  7. namespace Autodesk.AutoCAD.DatabaseServices
  8. {
  9.     public static class ExtensionMethods
  10.     {
  11.         static RXClass attDefClass = RXClass.GetClass(typeof(AttributeDefinition));
  12.  
  13.         public static void SynchronizeAttributes(this BlockTableRecord target)
  14.         {
  15.             if (target == null)
  16.                 throw new ArgumentNullException("target");
  17.  
  18.             Transaction tr = target.Database.TransactionManager.TopTransaction;
  19.             if (tr == null)
  20.                 throw new AcRx.Exception(ErrorStatus.NoActiveTransactions);
  21.             List<AttributeDefinition> attDefs = target.GetAttributes(tr);
  22.             foreach (ObjectId id in target.GetBlockReferenceIds(true, false))
  23.             {
  24.                 BlockReference br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
  25.                 br.ResetAttributes(attDefs, tr);
  26.             }
  27.             if (target.IsDynamicBlock)
  28.             {
  29.                 target.UpdateAnonymousBlocks();
  30.                 foreach (ObjectId id in target.GetAnonymousBlockIds())
  31.                 {
  32.                     BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
  33.                     attDefs = btr.GetAttributes(tr);
  34.                     foreach (ObjectId brId in btr.GetBlockReferenceIds(true, false))
  35.                     {
  36.                         BlockReference br = (BlockReference)tr.GetObject(brId, OpenMode.ForWrite);
  37.                         br.ResetAttributes(attDefs, tr);
  38.                     }
  39.                 }
  40.             }
  41.         }
  42.  
  43.         private static List<AttributeDefinition> GetAttributes(this BlockTableRecord target, Transaction tr)
  44.         {
  45.             List<AttributeDefinition> attDefs = new List<AttributeDefinition>();
  46.             foreach (ObjectId id in target)
  47.             {
  48.                 if (id.ObjectClass == attDefClass)
  49.                 {
  50.                     AttributeDefinition attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  51.                     attDefs.Add(attDef);
  52.                 }
  53.             }
  54.             return attDefs;
  55.         }
  56.  
  57.         private static void ResetAttributes(this BlockReference br, List<AttributeDefinition> attDefs, Transaction tr)
  58.         {
  59.             Dictionary<string, string> attValues = new Dictionary<string, string>();
  60.             foreach (ObjectId id in br.AttributeCollection)
  61.             {
  62.                 if (!id.IsErased)
  63.                 {
  64.                     AttributeReference attRef = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite);
  65.                     attValues.Add(attRef.Tag,
  66.                         attRef.IsMTextAttribute ? attRef.MTextAttribute.Contents : attRef.TextString);
  67.                     attRef.Erase();
  68.                 }
  69.             }
  70.             foreach (AttributeDefinition attDef in attDefs)
  71.             {
  72.                 AttributeReference attRef = new AttributeReference();
  73.                 attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  74.                 if (attDef.Constant)
  75.                 {
  76.                     attRef.TextString = attDef.IsMTextAttributeDefinition ?
  77.                         attDef.MTextAttributeDefinition.Contents :
  78.                         attDef.TextString;
  79.                 }
  80.                 else if (attValues.ContainsKey(attRef.Tag))
  81.                 {
  82.                     attRef.TextString = attValues[attRef.Tag];
  83.                 }
  84.                 br.AttributeCollection.AppendAttribute(attRef);
  85.                 tr.AddNewlyCreatedDBObject(attRef, true);
  86.             }
  87.         }
  88.     }
  89. }

VB code (have to clear the Root Namespace in the project to add these extension methods to Autodesk.AutoCAD.DatabaseServices)
Code - vb.net: [Select]
  1. Imports Autodesk.AutoCAD.DatabaseServices
  2. Imports Autodesk.AutoCAD.Runtime
  3.  
  4. Namespace Autodesk.AutoCAD.DatabaseServices
  5.     Public Module ExtensionMethods
  6.  
  7.         Dim attDefClass As RXClass = RXClass.GetClass(GetType(AttributeDefinition))
  8.  
  9.         <System.Runtime.CompilerServices.Extension> _
  10.         Public Sub SynchronizeAttributes(target As BlockTableRecord)
  11.             If target Is Nothing Then
  12.                 Throw New ArgumentNullException("target")
  13.             End If
  14.  
  15.             Dim tr As Transaction = target.Database.TransactionManager.TopTransaction
  16.             If tr Is Nothing Then
  17.                 Throw New Exception(ErrorStatus.NoActiveTransactions)
  18.             End If
  19.  
  20.             Dim attDefs As List(Of AttributeDefinition) = target.GetAttributes(tr)
  21.             For Each id As ObjectId In target.GetBlockReferenceIds(True, False)
  22.                 Dim br As BlockReference = _
  23.                     DirectCast(tr.GetObject(id, OpenMode.ForWrite), BlockReference)
  24.                 br.ResetAttributes(attDefs, tr)
  25.             Next
  26.             If target.IsDynamicBlock Then
  27.                 target.UpdateAnonymousBlocks()
  28.                 For Each id As ObjectId In target.GetAnonymousBlockIds()
  29.                     Dim btr As BlockTableRecord = _
  30.                         DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockTableRecord)
  31.                     attDefs = btr.GetAttributes(tr)
  32.                     For Each brId As ObjectId In btr.GetBlockReferenceIds(True, False)
  33.                         Dim br As BlockReference = _
  34.                             DirectCast(tr.GetObject(brId, OpenMode.ForWrite), BlockReference)
  35.                         br.ResetAttributes(attDefs, tr)
  36.                     Next
  37.                 Next
  38.             End If
  39.         End Sub
  40.  
  41.         <System.Runtime.CompilerServices.Extension> _
  42.         Private Function GetAttributes(target As BlockTableRecord, tr As Transaction) As List(Of AttributeDefinition)
  43.             Dim attdefs As List(Of AttributeDefinition) = New List(Of AttributeDefinition)
  44.             For Each id As ObjectId In target
  45.                 If id.ObjectClass = attDefClass Then
  46.                     Dim attDef As AttributeDefinition = _
  47.                         DirectCast(tr.GetObject(id, OpenMode.ForRead), AttributeDefinition)
  48.                     attdefs.Add(attDef)
  49.                 End If
  50.             Next
  51.             Return attdefs
  52.         End Function
  53.  
  54.         <System.Runtime.CompilerServices.Extension> _
  55.         Private Sub ResetAttributes(br As BlockReference, attDefs As List(Of AttributeDefinition), tr As Transaction)
  56.             Dim attValues As New Dictionary(Of String, String)()
  57.             For Each id As ObjectId In br.AttributeCollection
  58.                 If Not id.IsErased Then
  59.                     Dim attRef As AttributeReference = _
  60.                         DirectCast(tr.GetObject(id, OpenMode.ForWrite), AttributeReference)
  61.                     attValues.Add( _
  62.                         attRef.Tag, _
  63.                         If(attRef.IsMTextAttribute, attRef.MTextAttribute.Contents, attRef.TextString))
  64.                     attRef.Erase()
  65.                 End If
  66.             Next
  67.             For Each attDef As AttributeDefinition In attDefs
  68.                 Dim attRef As New AttributeReference()
  69.                 attRef.SetAttributeFromBlock(attDef, br.BlockTransform)
  70.                 If attDef.Constant Then
  71.                     attRef.TextString = If(attDef.IsMTextAttributeDefinition, _
  72.                                            attDef.MTextAttributeDefinition.Contents, _
  73.                                            attDef.TextString)
  74.                 Else If attValues IsNot Nothing AndAlso attValues.ContainsKey(attDef.Tag) Then
  75.                     attRef.TextString = attValues(attDef.Tag.ToUpper())
  76.                 End If
  77.                 br.AttributeCollection.AppendAttribute(attRef)
  78.                 tr.AddNewlyCreatedDBObject(attRef, True)
  79.             Next
  80.         End Sub
  81.  
  82.     End Module
  83.  
  84. End Namespace

This is fantastic gile. One thing though, is there any way to keep the "Prompt" field of the attribute definitions? I've slightly modified this to act as an attribute definition copy tool (to let me select a source block and copy its attribute definitions to another block). The attribute definitions seemingly keep most of their properties when transferred over, but the "Prompt" fields show up blank. Poking around in ObjectArx and the debugger, I don't see any obvious way to do this, but maybe there's something I'm missing.

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #31 on: December 05, 2021, 12:36:31 PM »
Gile, I'm using the method extension which doesn't seem to work, the defining attributes don't sync. I have to use the manual ATTSYNC command for the update to work. Will it be something with the block, because it is dynamic?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #32 on: December 06, 2021, 01:21:04 AM »
Hi kelcyo,

It worked fpr me with the drawing you linked. You should give more details (AutoCAD version, context, ...).
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #33 on: December 06, 2021, 04:31:46 PM »
Hi gile, I just insert the block into the uploaded drawing in places determined by a list of points. I'm working with current version 2022.

Code: [Select]
// Get the current UCS Z axis (extrusion direction)
Matrix3d ucsMat = ed.CurrentUserCoordinateSystem;
CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d;
Vector3d zdir = ucsMat.CoordinateSystem3d.Zaxis;

// Get the OCS corresponding to UCS Z axis
Matrix3d ocsMat = MakeOcs(zdir);

// Transform the input point from UCS to OCS
//Point3d pt = ppr.Value.TransformBy(ucsMat.PreMultiplyBy(ocsMat));

// Get the X axis of the OCS
Vector3d ocsXdir = ocsMat.CoordinateSystem3d.Xaxis;

// Get the UCS rotation (angle between the OCS X axis and the UCS X axis)
double rot = ocsXdir.GetAngleTo(ucs.Xaxis, zdir);

BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

// Foreach in points collection
foreach (Point3d item in _pcIns)
{
    BlockReference br = new BlockReference(item, bt["MDF"]);
    br.Position = item;
    br.Rotation = rot;
    br.Normal = zdir;
    btr.AppendEntity(br);
    tr.AddNewlyCreatedDBObject(br, true);   

}

ObjectIdCollection brefIds = btr.GetBlockReferenceIds(false, true);
foreach (ObjectId id in brefIds)
{
    BlockReference bref = (BlockReference)tr.GetObject(id, OpenMode.ForWrite, false, true);
    bref.RecordGraphicsModified(true);
}

btr.SynchronizeAttributes();

tr.Commit();
« Last Edit: December 07, 2021, 10:11:59 AM by kelcyo »

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #34 on: December 07, 2021, 07:04:25 PM »
Anyone have any tips how to solve the problem?

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #35 on: December 08, 2021, 01:38:30 AM »
Looking at your code, it looks like you're trying to synchronize the current Space (btr) attributes, not the "MDF" block definition.
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #36 on: December 08, 2021, 12:16:45 PM »
Even making your indicated change, there is an error in the timing, the attribute is not filled in correctly, it inserts the content of the standard block attribute and not the reference field...

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #37 on: December 08, 2021, 01:38:05 PM »
Here's a simple testing code:
Code - C#: [Select]
  1.     [CommandMethod("TEST")]
  2.     public static void Test()
  3.     {
  4.         var doc = AcAp.DocumentManager.MdiActiveDocument;
  5.         var db = doc.Database;
  6.         var ed = doc.Editor;
  7.         using (var tr = db.TransactionManager.StartTransaction())
  8.         {
  9.             var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  10.             if (bt.Has("MDF"))
  11.             {
  12.                 var btr = (BlockTableRecord)tr.GetObject(bt["MDF"], OpenMode.ForRead);
  13.                 btr.SynchronizeAttributes();
  14.             }
  15.             tr.Commit();
  16.         }
  17.         ed.Regen();
  18.     }

Here's a screencast which shows this code in action with your block: https://autode.sk/3EExoV1
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #38 on: December 08, 2021, 06:44:57 PM »
It's not working correctly with me, the attributes remain with the wrong content...



Code: [Select]
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
if (!bt.Has("MDF"))
{
    ed.WriteMessage("\nBloco modelo não localizado.");
    return;
}


// Get the current UCS Z axis (extrusion direction)
Matrix3d ucsMat = ed.CurrentUserCoordinateSystem;
CoordinateSystem3d ucs = ucsMat.CoordinateSystem3d;
Vector3d zdir = ucsMat.CoordinateSystem3d.Zaxis;

// Get the OCS corresponding to UCS Z axis
Matrix3d ocsMat = MakeOcs(zdir);

// Transform the input point from UCS to OCS
//Point3d pt = ppr.Value.TransformBy(ucsMat.PreMultiplyBy(ocsMat));

// Get the X axis of the OCS
Vector3d ocsXdir = ocsMat.CoordinateSystem3d.Xaxis;

// Get the UCS rotation (angle between the OCS X axis and the UCS X axis)
double rot = ocsXdir.GetAngleTo(ucs.Xaxis, zdir);

BlockTableRecord curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

// Foreach in points collection
foreach (PointsLocation item in _pcIns)
{
   
    BlockReference br = new BlockReference(item._ponto, bt["MDF"]);
    br.Position = item._ponto;
    br.Rotation = rot;   
    br.Normal = zdir;

    ObjectId _newid = curSpace.AppendEntity(br);
    tr.AddNewlyCreatedDBObject(br, true);


    if (br.IsDynamicBlock && ids.Length == 2)
    {
        DynamicBlockReferencePropertyCollection props = br.DynamicBlockReferencePropertyCollection;

        foreach (DynamicBlockReferenceProperty prop in props)
        {
            object[] values = prop.GetAllowedValues();


            if (item._ordem == 0)
            {
                //Switch Property
                switch (prop.PropertyName)
                {
case "Distancia_Texto":
    prop.Value = 18.0;
    break;
case "Anglo_Texto":
    prop.Value = 45.0;
    break;
case "Anglo_Marco":
    prop.Value = 0.0;
    break;
                }

            }
            else
            {
                switch (prop.PropertyName)
                {
case "Distancia_Texto":
    prop.Value = 18.0;
    break;
case "Anglo_Texto":
    prop.Value = 225.0;
    break;
case "Anglo_Marco":
    prop.Value = 0.0;
    break;
                }
            }

        }
    }   

}

var btr = (BlockTableRecord)tr.GetObject(bt["MDF"], OpenMode.ForRead);
btr.SynchronizeAttributes();

tr.Commit();

https://autode.sk/3GuUNJ0

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #39 on: December 09, 2021, 01:50:07 AM »
Your problem is not due to the SynchronizeAttribute method (which does not seem to be rquired as you do not change attributes properties).
Your code simply does not add attribute references to the block reference. While insertin an attributed block reference, you have to explicitly create the attribute references. See this topic.
« Last Edit: December 09, 2021, 04:18:21 AM by gile »
Speaking English as a French Frog

kelcyo

  • Mosquito
  • Posts: 7
Re: .NET BLOCK Routines
« Reply #40 on: December 09, 2021, 02:14:29 PM »
Gile, I was really wrong... I needed to add the attribute references, but the detail was found in the inclusion of the field within the attribute. This was perfect, thanks for the tip...

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #41 on: December 09, 2021, 02:34:27 PM »
Here's an example:
Code - C#: [Select]
  1. [CommandMethod("TEST")]
  2. public static void Test()
  3. {
  4.     var doc = AcAp.DocumentManager.MdiActiveDocument;
  5.     var db = doc.Database;
  6.     var ed = doc.Editor;
  7.     using (var tr = db.TransactionManager.StartTransaction())
  8.     {
  9.         var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  10.         if (bt.Has("MDF"))
  11.         {
  12.             var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  13.             var br = new BlockReference(new Point3d(30.0, 15.0, 0.0), bt["MDF"]);
  14.             curSpace.AppendEntity(br);
  15.             tr.AddNewlyCreatedDBObject(br, true);
  16.  
  17.             // add attribute references to the block reference
  18.             var btr = (BlockTableRecord)tr.GetObject(bt["MDF"], OpenMode.ForRead);
  19.             foreach (ObjectId id in btr)
  20.             {
  21.                 if (id.ObjectClass.DxfName == "ATTDEF")
  22.                 {
  23.                     var attDef = (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  24.                     if (!attDef.Constant)
  25.                     {
  26.                         var attRef = new AttributeReference();
  27.                         attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  28.                         br.AttributeCollection.AppendAttribute(attRef);
  29.                         tr.AddNewlyCreatedDBObject(attRef, true);
  30.                         if (attRef.HasFields)
  31.                         {
  32.                             attRef.TextString = attRef.getTextWithFieldCodes().Replace(
  33.                                 "?BlockRefId",
  34.                                 $@"%<\_ObjId {br.ObjectId.ToString().Trim('(', ')')}>%");
  35.                         }
  36.  
  37.                     }
  38.                 }
  39.             }
  40.  
  41.             // set dynamic properties
  42.             foreach (DynamicBlockReferenceProperty prop in br.DynamicBlockReferencePropertyCollection)
  43.             {
  44.                 switch (prop.PropertyName)
  45.                 {
  46.                     case "Distancia_Texto":
  47.                         prop.Value = 18.0;
  48.                         break;
  49.                     case "Anglo_Texto":
  50.                         prop.Value = 45.0 * Math.PI / 180.0;
  51.                         break;
  52.                     default:
  53.                         break;
  54.                 }
  55.             }
  56.         }
  57.         tr.Commit();
  58.     }
  59.     ed.Regen();
  60. }
Speaking English as a French Frog