Block reference without inserting or selecting

Block reference without inserting or selecting

Rob.O
Collaborator Collaborator
1,988 Views
10 Replies
Message 1 of 11

Block reference without inserting or selecting

Rob.O
Collaborator
Collaborator

Hi all!

 

I need help (in more ways than one my wife would say).

 

I am trying to get an existing block reference in my current document without a selection set and without inserting the block. So in other words, the block in question has previously been inserted into the drawing. I have found numerous examples on the forums, but all deal with either first inserting the block or using a selection set to get at it.

 

Everything works great until I try to declare myBlkRef.  Everytime I run the code, I get an "InvalidCastException" error on the second line of code shown below.  If I run the same code when using a selection set to select the block the code works fine. 

 

I have no problem getting the blocktable, blocktablerecord or objectid of the block in question, but I cannot figure out how to cast it as a blockreference.

 

So, my basic question is, how do I get a blockreference that has already been inserted into a drawing without using a selection set??? My ultimate goal is to change it's attribute values.

 

Please forgive my ignorance... I am still trying to learn all of this blocktable, blocktablerecord, blockreference, objectid, etc... business.

 

Dim blkRefID As ObjectId

Dim myBlkRef As BlockReference = blkRefID.GetObject(OpenMode.ForRead)

 

TIA!

0 Likes
Accepted solutions (2)
1,989 Views
10 Replies
Replies (10)
Message 2 of 11

Anonymous
Not applicable

So the BlockTable holds all the BlockTableRecord's.  Once you are able to get the BTR, then you will be able to get all the BlockReference's from there, as the BTR has a method called GetBlockReferenceIds, which will return an ObjectIdCollection.  You can just step through the collection with a Foreach statement, and cast those ids to BlockReference's.

 

Hope that helps.

0 Likes
Message 3 of 11

Anonymous
Not applicable
Accepted solution

This is exactly what t.willey said in the previous post, but also moves the blockreferences from a block named "C" for no reason at all

 

 

  <CommandMethod("GetAndMoveBlockReference")> _
        Public Sub GetAndMoveBlockReference()
            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim db As Database = doc.Database
            Dim ed As Editor = doc.Editor
            Using trx As Transaction = db.TransactionManager.StartTransaction()


                Dim bt As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead)
                Dim btrBlock As BlockTableRecord = bt("C").GetObject(OpenMode.ForRead)
                Dim brefIds As ObjectIdCollection = btrBlock.GetBlockReferenceIds(TrueTrue)

                For Each objId As ObjectId In brefIds
                    Dim bref As BlockReference = objId.GetObject(OpenMode.ForWrite)
                    bref.Position = bref.Position.TransformBy(Matrix3d.Displacement(New Vector3d(25, 25, 0)))
                Next

                trx.Commit()

            End Using

        End Sub
0 Likes
Message 4 of 11

Rob.O
Collaborator
Collaborator

Thanks folks!  The above worked perfectly!

 

Now on to my next question... the above code works great for a single block, but what if I want to itterate more than one block?  Assuming there is only one Objectid in the ObjectidCollection as we told the BlockTable variable to hold "C".

 

Here is my code so you can see what I am trying to accomplish...

Using acTransTB As Transaction = acDocTB.TransactionManager.StartTransaction
            Try

                Dim acBT As BlockTable = acCurDBTB.BlockTableId.GetObject(OpenMode.ForRead)
                Dim acBTR As BlockTableRecord = acBT("ECDT_JobName").GetObject(OpenMode.ForRead)
                Dim acObjIds As ObjectIdCollection = acBTR.GetBlockReferenceIds(True, True)

                For Each acObjId As ObjectId In acObjIds
                    Dim acBref As BlockReference = acObjId.GetObject(OpenMode.ForWrite)
                    Dim myAttValsTB As New Dictionary(Of String, String)

                    If acBref.Name = "ECDT_JobName" Then
                        myAttValsTB.Add("JOB_NAME", strGenJobName.ToUpper)
                    End If

                    If acBref.Name = "ECDT_JobNo" Then
                        myAttValsTB.Add("JOB_NUMBER", strGenJobNumber.ToUpper)
                    End If

                    Dim myAttCollection As AttributeCollection = acBref.AttributeCollection
                    For Each myAttRefID As ObjectId In myAttCollection
                        Dim myAttRef As AttributeReference = myAttRefID.GetObject(OpenMode.ForWrite)
                        If myAttValsTB.ContainsKey(myAttRef.Tag) Then
                            myAttRef.TextString = myAttValsTB(myAttRef.Tag)
                        End If
                    Next
                Next
                acTransTB.Commit()
                
            Catch 
            Finally                
            End Try

        End Using

 

 

0 Likes
Message 5 of 11

Anonymous
Not applicable

How many objectId's in ObjectidCollection depends on how many times that block is inserted 0 to whatever

 

If you want different blocks - a simple basic way is to itterate through the blocktable and check if the BlocktableRecord.IsLayout is false

 

This does nothing but shows idea

 


        <CommandMethod("AllBlocksAndReferences")> _
        Public Sub AllBlocksAndReferences()

            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim db As Database = doc.Database
            Dim ed As Editor = doc.Editor

            Using trx As Transaction = db.TransactionManager.StartTransaction

                Dim bt As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead)
                For Each objId As ObjectId In bt
                    Dim btr As BlockTableRecord = objId.GetObject(OpenMode.ForRead)
                    If Not btr.IsLayout Then
                        Dim objIdColl As ObjectIdCollection = btr.GetBlockReferenceIds(TrueTrue)
                    End If
                Next
            End Using
        End Sub

 

 

Here is something simple I just posted for someone about Sending Block Name and Count to Excel using COM that uses same idea

 

 

  [CommandMethod("ExcelAndCom")]
        public void ExcelAndCom()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook wb = excel.Workbooks.Add("");
            Microsoft.Office.Interop.Excel.Worksheet ws = excel.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet;
            ws.Range["A1"].Value = "Block Name";
            ws.Range["B1"].Value = "Block Count";           
            int row = 1;
            using (Transaction trx = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = db.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                foreach (ObjectId objId in bt)
                {
                    BlockTableRecord btr = objId.GetObject(OpenMode.ForRead) as BlockTableRecord;
                    if (!(btr.IsLayout))
                    {
                        row++;
                        int coloum = 1;
                        ws.Cells[row, coloum] = btr.Name;
                        ObjectIdCollection objIdColl = btr.GetBlockReferenceIds(true, true);
                        coloum++;
                        ws.Cells[row, coloum] = objIdColl.Count.ToString();
                    }
                }
                
            }
            excel.Visible = true;


        }

 

 

 

 

 

0 Likes
Message 6 of 11

Rob.O
Collaborator
Collaborator

Strange... for some reason when I run the code with two different block references in the drawing, it only picks up the first one it finds. So basically the objIdColl variable only has a count of 1.  If I make one copy of the block that it finds and run the code again the count is 2, but its only counting the duplicate blocks and not the separate block reference.  Am I doing something wrong?

0 Likes
Message 7 of 11

Anonymous
Not applicable

Are the block dynamic, older demand loaded etc.....

 

Load up a dwg with some of the blocks you are refering to

0 Likes
Message 8 of 11

Rob.O
Collaborator
Collaborator

I created a new drawing and added the two blocks.  Both are simple attribute blocks.  If I run the code with both blocks in the drawing, neither of the attribute values are updated.  But if I delete one of the blocks and purge the drawing and then run the code, the attribute value of the remaining block updates as expected.

 

Drawing is attached.

 

Code is here:

 

Public Class UpdateAttribute

    <CommandMethod("AllBlocksAndReferences")> _
    Public Sub AllBlocksAndReferences()

        Dim doc As Document = Application.DocumentManager.MdiActiveDocument
        Dim db As Database = doc.Database
        Dim ed As Editor = doc.Editor

        Using trx As Transaction = db.TransactionManager.StartTransaction

            Dim bt As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead)
            Dim objIdColl As ObjectIdCollection
            For Each objId As ObjectId In bt
                Dim btr As BlockTableRecord = objId.GetObject(OpenMode.ForRead)
                If Not btr.IsLayout Then
                    objIdColl = btr.GetBlockReferenceIds(True, True)
                End If
            Next
            For Each objId In objIdColl
                Dim acBref As BlockReference = objId.GetObject(OpenMode.ForWrite)
                MsgBox(acBref.Name)
                Dim myAttValsTB As New Dictionary(Of String, String)

                If acBref.Name = "ECDT_JobNo" Then
                    myAttValsTB.Add("JOB_NAME", "this is a test-Job Number")
                End If

                If acBref.Name = "ECDT_JobName" Then
                    myAttValsTB.Add("JOB_NAME", "this is a test-Job Name")
                End If

                Dim myAttCollection As AttributeCollection = acBref.AttributeCollection
                For Each myAttRefID As ObjectId In myAttCollection
                    Dim myAttRef As AttributeReference = myAttRefID.GetObject(OpenMode.ForWrite)
                    If myAttValsTB.ContainsKey(myAttRef.Tag) Then
                        myAttRef.TextString = myAttValsTB(myAttRef.Tag)
                    End If
                Next
            Next
            trx.Commit()
        End Using
    End Sub

End Class

 

 

0 Likes
Message 9 of 11

Anonymous
Not applicable
Accepted solution

You are iterating through the block table and setting objIdColl to each Blocks  blockreferences

then after the last one is when your code begins to modify

 

 

Just made a qick change

 <CommandMethod("AllBlocksAndReferences")> _
        Public Sub AllBlocksAndReferences()

            Dim doc As Document = Application.DocumentManager.MdiActiveDocument
            Dim db As Database = doc.Database
            Dim ed As Editor = doc.Editor

            Using trx As Transaction = db.TransactionManager.StartTransaction

                Dim bt As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead)

                For Each btrObjId As ObjectId In bt

                    Dim btr As BlockTableRecord = btrObjId.GetObject(OpenMode.ForRead)

                    If btr.IsLayout Then
                        Continue For
                    End If

                    Dim objIdColl As ObjectIdCollection = btr.GetBlockReferenceIds(TrueTrue)

                    For Each objId In objIdColl

                        Dim acBref As BlockReference = objId.GetObject(OpenMode.ForWrite)
                        MsgBox(acBref.Name)
                        Dim myAttValsTB As New Dictionary(Of StringString)

                        If acBref.Name = "ECDT_JobNo" Then
                            myAttValsTB.Add("JOB_NUMBER""this is a test-Job Number")
                        End If

                        If acBref.Name = "ECDT_JobName" Then
                            myAttValsTB.Add("JOB_NAME""this is a test-Job Name")
                        End If

                        Dim myAttCollection As AttributeCollection = acBref.AttributeCollection
                        For Each myAttRefID As ObjectId In myAttCollection
                            Dim myAttRef As AttributeReference = myAttRefID.GetObject(OpenMode.ForWrite)
                            If myAttValsTB.ContainsKey(myAttRef.Tag) Then
                                myAttRef.TextString = myAttValsTB(myAttRef.Tag)
                            End If
                        Next
                    Next
                Next
                trx.Commit()
            End Using
        End Sub
0 Likes
Message 10 of 11

Anonymous
Not applicable

Also change 

"JOB_NAME"

to

 

"JOB_NUMBER"

in the first one for both to change

0 Likes
Message 11 of 11

Rob.O
Collaborator
Collaborator

PERFECT!!!

 

Thanks so much for all of your help on this!  I will be sure to look you up when I need more assistance!!! Smiley Happy

0 Likes