Change Attributes of a block nested in dynamic block

Change Attributes of a block nested in dynamic block

jplujan
Advocate Advocate
2,436 Views
12 Replies
Message 1 of 13

Change Attributes of a block nested in dynamic block

jplujan
Advocate
Advocate

Hi:
I want to change the attributes of a block contained in a dynamic block.

I have managed to change the attributes when the block is static.

I leave the code I am using.

 

 Sub UpdateAtts1(BlockID As Object, NombreBloqueHijo As String, prefijo As String, tagstring As String, contador As Integer)
        Dim doc As Document = Application.DocumentManager.MdiActiveDocument
        Dim db As Database = doc.Database
        Dim ed As Editor = doc.Editor
        Dim i As Integer = contador

        Using docloc As DocumentLock = doc.LockDocument
            Using tr As Transaction = db.TransactionManager.StartTransaction()
                Dim br As BlockReference = CType(tr.GetObject(BlockID, OpenMode.ForRead), BlockReference)
                Dim btr As BlockTableRecord = Nothing
                If br.IsDynamicBlock Then
                    Dim btrHijo As BlockTableRecord = CType(tr.GetObject(br.DynamicBlockTableRecord, OpenMode.ForRead), BlockTableRecord)
                    For Each id As ObjectId In btrHijo
                        If id.ObjectClass.Name = "AcDbBlockReference" Then
                            Dim brHijo As BlockReference = DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockReference)
                            If brHijo.Name = NombreBloqueHijo Then
                                For Each attId As ObjectId In brHijo.AttributeCollection
                                    Dim attRef As AttributeReference = DirectCast(tr.GetObject(attId, OpenMode.ForWrite), AttributeReference)
                                    If attRef.Tag = tagstring Then
                                        attRef.UpgradeOpen()
                                        attRef.TextString = prefijo & i
                                        i += 1
                                        Exit For
                                    End If
                                Next
                            End If
                        End If
                    Next

                Else
                    btr = CType(tr.GetObject(br.BlockTableRecord, OpenMode.ForWrite), BlockTableRecord)
                    For Each id As ObjectId In btr
                        If id.ObjectClass.Name = "AcDbBlockReference" Then
                            Dim brHijo As BlockReference = DirectCast(tr.GetObject(id, OpenMode.ForRead), BlockReference)
                            If brHijo.Name = NombreBloqueHijo Then
                                For Each attId As ObjectId In brHijo.AttributeCollection
                                    Dim attRef As AttributeReference = DirectCast(tr.GetObject(attId, OpenMode.ForWrite), AttributeReference)
                                    If attRef.Tag = tagstring Then
                                        attRef.UpgradeOpen()
                                        attRef.TextString = prefijo & i
                                        i += 1
                                        Exit For
                                    End If
                                Next
                            End If
                        End If
                    Next
                End If
                tr.Commit()
            End Using
        End Using
        ed.Regen()
    End Sub


Thank you


Greetings

Accepted solutions (1)
2,437 Views
12 Replies
Replies (12)
Message 2 of 13

Norman_Yuan
Mentor
Mentor

Well, you did not describe how the property/properties in the dynamic block definition is designed or how it/they behave, especially how it could affect the said BlockReference in the dynamic block definition. For example, if the block definition has a Visibility parameter that could make the BlockReference element shown/hidden, then the nested block reference could only be found in the anonymous block definition.

 

That is, in your code:

 

If br.IsDynamicBlock Then

    Dim btrHijo As BlockTablerecord = CType(tr.GetObject(br.DynamicBlockTableRecord, ...

Else

  ....

 

Should be

 

If br.IsDynamicBlock Then

    Dim btrHijo As BlockTablerecord = CType(tr.GetObject(br.BlockTableRecord, ...

Else

  ....

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 13

jplujan
Advocate
Advocate

Thanks for your quick response


I just made your change and it doesn't update the attributes.

I enclose an example drawing I want to renumber the blocks called "ET-V" in the code "NombreBloqueHijo" with a consecutive order.

 

Thanks, again

0 Likes
Message 4 of 13

Norman_Yuan
Mentor
Mentor

Well, I have no problem update the nested block reference's attribute in both the static block and dynamic blocks in your sample drawing with the exactly the same code, as following:

 

 

 

    public class MyCommands
    {
        [CommandMethod("AttCmd")]
        public static void RunMyCommand()
        {
            var doc = CadApp.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            var blkId = SelectBlock(ed);
            if (!blkId.IsNull)
            {
                UpdateNestedBlockAttributes(blkId);
                // Regen is needed, since the block definition is update
                // (that is, the nested block reference is in the block definition)
                ed.Regen();
            }
        }

        private static ObjectId SelectBlock(Editor ed)
        {
            var opt = new PromptEntityOptions("\nSelect block:");
            opt.SetRejectMessage("\nInbalid selection.");
            opt.AddAllowedClass(typeof(BlockReference), true);
            var res = ed.GetEntity(opt);
            if (res.Status== PromptStatus.OK)
            {
                return res.ObjectId;
            }
            else
            {
                return ObjectId.Null;
            }
        }

        private static void UpdateNestedBlockAttributes(ObjectId blkId)
        {
            using (var tran = blkId.Database.TransactionManager.StartTransaction())
            {
                var blkRef = (BlockReference)tran.GetObject(blkId, OpenMode.ForRead);
                var blkDef = (BlockTableRecord)tran.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead);
                foreach (ObjectId id in blkDef)
                {
                    if (id.ObjectClass.DxfName.ToUpper()=="INSERT")
                    {
                        var nestedBlkRef = (BlockReference)tran.GetObject(id, OpenMode.ForRead);
                        if (nestedBlkRef.AttributeCollection.Count>0)
                        {
                            int index = 1;
                            foreach (ObjectId attId in nestedBlkRef.AttributeCollection)
                            {
                                var attref = (AttributeReference)tran.GetObject(attId, OpenMode.ForWrite);
                                attref.TextString = $"NUMBER: {index}";
                                index++;
                            }
                        }
                    }
                }
                tran.Commit();
            }
        }
    }

 

 

 

Below is the video clip showing the code effect:

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 13

Norman_Yuan
Mentor
Mentor

I have no problem to update attribute values in block reference that is nested in a block definition, be it static or dynamic, with the exactly the same code:

 

 

    public class MyCommands
    {
        [CommandMethod("AttCmd")]
        public static void RunMyCommand()
        {
            var doc = CadApp.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            var blkId = SelectBlock(ed);
            if (!blkId.IsNull)
            {
                UpdateNestedBlockAttributes(blkId);
                // Regen is needed, since the block definition is update
                // (that is, the nested block reference is in the block definition)
                ed.Regen();
            }
        }

        private static ObjectId SelectBlock(Editor ed)
        {
            var opt = new PromptEntityOptions("\nSelect block:");
            opt.SetRejectMessage("\nInbalid selection.");
            opt.AddAllowedClass(typeof(BlockReference), true);
            var res = ed.GetEntity(opt);
            if (res.Status== PromptStatus.OK)
            {
                return res.ObjectId;
            }
            else
            {
                return ObjectId.Null;
            }
        }

        private static void UpdateNestedBlockAttributes(ObjectId blkId)
        {
            using (var tran = blkId.Database.TransactionManager.StartTransaction())
            {
                var blkRef = (BlockReference)tran.GetObject(blkId, OpenMode.ForRead);
                var blkDef = (BlockTableRecord)tran.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead);
                foreach (ObjectId id in blkDef)
                {
                    if (id.ObjectClass.DxfName.ToUpper()=="INSERT")
                    {
                        var nestedBlkRef = (BlockReference)tran.GetObject(id, OpenMode.ForRead);
                        if (nestedBlkRef.AttributeCollection.Count>0)
                        {
                            int index = 1;
                            foreach (ObjectId attId in nestedBlkRef.AttributeCollection)
                            {
                                var attref = (AttributeReference)tran.GetObject(attId, OpenMode.ForWrite);
                                attref.TextString = $"NUMBER: {index}";
                                index++;
                            }
                        }
                    }
                }
                tran.Commit();
            }
        }
    }

 

 

See the video clip below for the code effect:

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 6 of 13

jplujan
Advocate
Advocate

Hi:
Thanks for your reply

I'm going to try it

Thanks again

0 Likes
Message 7 of 13

jplujan
Advocate
Advocate

Hi
The code works for all nested blocks.
I have modified the code so that it only renumber the block called "ET-V" and it does not work

I leave the new code and a new drawing

 

Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput


Public Class Mycommands

    <CommandMethod("AttCmd")>
    Public Shared Sub RunMyCommand()
        Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
        Dim ed = doc.Editor
        Dim blkId = Mycommands.SelectBlock(ed)
        If Not blkId.IsNull Then
            Mycommands.UpdateNestedBlockAttributes(blkId)
            ' Regen is needed, since the block definition is update
            ' (that is, the nested block reference is in the block definition)
            ed.Regen
        End If

    End Sub

    Private Shared Function SelectBlock(ByVal ed As Editor) As ObjectId
        Dim opt = New PromptEntityOptions("" & vbLf & "Select block:")
        opt.SetRejectMessage("" & vbLf & "Invalid selection.")
        opt.AddAllowedClass(GetType(BlockReference), True)
        Dim res = ed.GetEntity(opt)
        If (res.Status = PromptStatus.OK) Then
            Return res.ObjectId
        Else
            Return ObjectId.Null
        End If

    End Function

    Private Shared Sub UpdateNestedBlockAttributes(ByVal blkId As ObjectId)
        Dim tran = blkId.Database.TransactionManager.StartTransaction
        Dim blkRef = CType(tran.GetObject(blkId, OpenMode.ForRead), BlockReference)
        Dim blkDef = CType(tran.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead), BlockTableRecord)

        For Each id As ObjectId In blkDef
            If (id.ObjectClass.DxfName.ToUpper = "INSERT") Then
                Dim nestedBlkRef = CType(tran.GetObject(id, OpenMode.ForRead), BlockReference)
                ' Block Name "ET-V"
                If nestedBlkRef.Name = "ET-V" Then

                    If (nestedBlkRef.AttributeCollection.Count > 0) Then
                        Dim index As Integer = 1
                        For Each attId As ObjectId In nestedBlkRef.AttributeCollection
                            Dim attref = CType(tran.GetObject(attId, OpenMode.ForWrite), AttributeReference)
                            attref.TextString = "ET-{index}"

                            index = (index + 1)
                        Next
                    End If

                End If
            End If

        Next
        tran.Commit
    End Sub
End Class

Thank you

0 Likes
Message 8 of 13

Norman_Yuan
Mentor
Mentor

I am sure that you know the ONLY change you made is this:

 

... ...

If nestedBlkRef.Name = "ET-V" Thern

    ... ...

End If 

... ...

 

So, if the code does not work as expected, then that change is the reason: BECAUSE the block reference is dynamic, so, its "Name" property is very likely not the same as the dynamic block definition's Name, rather it is the name of an anonymous block definition, which is generated by AutoCAD when a dynamic property value is set to a value different from the default value. So, if you need to determine if the dynamic block reference is the target one (containing target attributes) by its name, you need to find its dynamic block definition's name. Some thing like:

 

For Each ....

    If id.ObjectClass.DxFName.ToUpper = "INSERT" Then

        Dim nestedBlkRef = CType(tran.GetObject(id, OpenMode.ForRead), BlockReference)

        Dim nestedBr = CType(tran.GetObject(

                 nestedBlkRef.DynamicBlockTableRecord, OpenMode.ForRead), BlockTableRecord)

        Dim blkName As String = nestedBlkBr.Name

        If blkName.ToUpper = "ET-V" Then

            '' Now you have really identified the target block reference

            '' loop through its attributes and do update according to its tag here.

        End If

    End If

 

If you do know/are sure that there is not other block in the drawing that contains a nested block reference, which in turn contains attributes with specific TAGs (chances are very low, I'd think), you probably do not need to test the nested block reference's effective block name. 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 9 of 13

jplujan
Advocate
Advocate

Hello again.

I can not get the result, I do not want to take more of your time on the subject, I think the solution is close, I will continue investigating.

Thank you

0 Likes
Message 10 of 13

Norman_Yuan
Mentor
Mentor
Accepted solution

Well, I just added a couple of lines of code to test block reference's "effective name", as I suggested, and it works as expected. Here are the working code and video clip showing the effect:

 

 

    public class MyCommands
    {
        [CommandMethod("AttCmd")]
        public static void RunMyCommand()
        {
            var doc = CadApp.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            var blkId = SelectBlock(ed);
            if (!blkId.IsNull)
            {
                UpdateNestedBlockAttributes(blkId);
                // Regen is needed, since the block definition is update
                // (that is, the nested block reference is in the block definition)
                ed.Regen();
            }
        }

        private static ObjectId SelectBlock(Editor ed)
        {
            var opt = new PromptEntityOptions("\nSelect block:");
            opt.SetRejectMessage("\nInbalid selection.");
            opt.AddAllowedClass(typeof(BlockReference), true);
            var res = ed.GetEntity(opt);
            if (res.Status== PromptStatus.OK)
            {
                return res.ObjectId;
            }
            else
            {
                return ObjectId.Null;
            }
        }

        private static void UpdateNestedBlockAttributes(ObjectId blkId)
        {
            using (var tran = blkId.Database.TransactionManager.StartTransaction())
            {
                var blkRef = (BlockReference)tran.GetObject(
                    blkId, OpenMode.ForRead);
                var blkDef = (BlockTableRecord)tran.GetObject(
                    blkRef.BlockTableRecord, OpenMode.ForRead);
                int index = 1;

                foreach (ObjectId id in blkDef)
                {
                    if (id.ObjectClass.DxfName.ToUpper() == "INSERT")
                    {
                        var nestedBlkRef = (BlockReference)tran.GetObject(
                            id, OpenMode.ForRead);
                        var nestedBlkDef = (BlockTableRecord)tran.GetObject(
                            nestedBlkRef.DynamicBlockTableRecord, OpenMode.ForRead);
                        if (nestedBlkDef.Name.ToUpper() == "ET-V")
                        {
                            if (nestedBlkRef.AttributeCollection.Count > 0)
                            {
                                foreach (ObjectId attId in nestedBlkRef.AttributeCollection)
                                {
                                    var attref = 
                                        (AttributeReference)tran.GetObject(attId, OpenMode.ForWrite);
                                    switch(attref.Tag.ToUpper())
                                    {
                                        case "NUMVALVULA":
                                            attref.TextString = 
                                                $"VAL-{index.ToString().PadLeft(3,'0')}";
                                            break;
                                        case "DESCRIPCION":
                                            attref.TextString = 
                                                $"DESCRIPCION: {index.ToString().PadLeft(3, '0')}";
                                            break;
                                        case "ELEMENTO":
                                            attref.TextString = 
                                                $"ELEMENTO: {index.ToString().PadLeft(3, '0')}";
                                            break;
                                    }
                                    
                                    index++;
                                }
                            }
                        }
                    }
                }

                tran.Commit();
            }
        }
    }

 

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 11 of 13

jplujan
Advocate
Advocate

Hi
Very grateful for your dedication.

Now if it works correctly, what I have realized is that if I need to edit the main dynamic block, when it records it it does not keep the changes made in the children.

Thank you

0 Likes
Message 12 of 13

Norman_Yuan
Mentor
Mentor

I am not sure what does "... edit the main dynamic block, when it records it it does not keep the changes made in the children..." mean. But having dynamic block nested inside a dynamic block would not be a good practice as far as I can see.

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 13 of 13

jplujan
Advocate
Advocate

Hi:

I understand, we left the subject for ditch.

Thank you so much for everything

0 Likes