Author Topic: Layouts, Blocks, Attributes...  (Read 10248 times)

0 Members and 1 Guest are viewing this topic.

vegbruiser

  • Guest
Layouts, Blocks, Attributes...
« on: July 01, 2008, 05:43:36 AM »
Hi,
   I've been struggling with the following problem for a couple of weeks and I wondered if one of you fine chaps would be able to point me in the right direction:

In VBA, if I want to find and edit specific attributes contained within the layouts of a drawing I'd simply do this: -

Code: [Select]
Set layouts = thisdrawing.layouts
for each layout in layouts
 Thisdrawing.activelayout = layout
 If thisdrawing.activelayout.name <> "Model" then
  For each Bobj in thisdrawing.paperspace
   If Bobj.objectname = "AcDbBlockreference" Then
    varattributes = bobj.getattributes
    for i = lbound(varattributes) to ubound(varattributes)
     if ucase(varattributes(i).TagString = ucase("date") then
      varattributes(i).textstring = "some value"
     end if
    next i
   End if
  Next
 end if
next

Now, I realise that the .NET way of doing this is almost completely different, and, having paid for the latest .NET book by Jerry Winters, I am beginning to get to grips with the problem, but am still having a hard time getting the .NET equivalent of the above code working.

As I understand it, having searched both the Autodesk .NET newsgroups, and this very forum is that I need to search the Blocktablerecords and determine whether the various blockreferences I find are part of a layout, but having implemented the following code, it doesn't function as I'd have expected: -

Code: [Select]
<CommandMethod("UTB")> _
        Public Sub UpdateTitleBlock()
            Dim db As Database = HostApplicationServices.WorkingDatabase
            Using trans As Transaction = db.TransactionManager.StartTransaction()
                Try
                    Dim bt As BlockTable = DirectCast(trans.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable)
                    Dim btr As BlockTableRecord = DirectCast(trans.GetObject(bt(BlockTableRecord.PaperSpace), OpenMode.ForRead), BlockTableRecord)
                    For Each bId As ObjectId In btr
                        Using ent As Entity = DirectCast(trans.GetObject(bId, OpenMode.ForRead, False), Entity)
                            If ent.GetRXClass().Name.ToString() = "AcDbBlockReference" Then
                                Dim br As BlockReference = DirectCast(ent, BlockReference)
                                Dim blkObj As BlockTableRecord = DirectCast(trans.GetObject(br.BlockTableRecord, OpenMode.ForRead), BlockTableRecord)
                                If blkObj.IsDynamicBlock = False Then
                                    btr = DirectCast(trans.GetObject(br.BlockTableRecord, OpenMode.ForRead), BlockTableRecord)
                                Else
                                    btr = DirectCast(trans.GetObject(br.DynamicBlockTableRecord, OpenMode.ForRead), BlockTableRecord)
                                End If
                                MsgBox(btr.Name & " : " & vbTab & blkObj.Name) ' these end up the same?
                                If (blkObj.HasAttributeDefinitions) AndAlso (btr.Name = "Drawing Border") Then
                                    Dim attcol As Autodesk.AutoCAD.DatabaseServices.AttributeCollection = br.AttributeCollection
                                    For Each attId As ObjectId In attcol
                                        Dim attRef As AttributeReference = DirectCast(trans.GetObject(attId, OpenMode.ForWrite), AttributeReference)
                                        MsgBox(attRef.Tag & " : " & vbTab & attRef.TextString)
                                    Next
                                End If
                            End If
                        End Using
                    Next
                    trans.Commit()
                Catch ex As System.Exception
                    System.Windows.Forms.MessageBox.Show(ex.ToString())
                    MessageBox.Show("Unexpected Error: " + ex.ToString())
                End Try
            End Using
        End Sub
Given what I've learnt reading Jerry's very informative book, this should be able to at least list the attributes contained within the block called drawing border, but it doesn't work. Any suggestions?

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #1 on: July 01, 2008, 06:17:26 AM »
What version of AutoCAD are you using?

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #2 on: July 01, 2008, 06:18:04 AM »
Also, what version of Visual Studio (if any) and Framework version are you targeting?

vegbruiser

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #3 on: July 01, 2008, 06:20:47 AM »
:oops: It's for AutoCAD 2006 and .NET Framework 3.5 - I'm using VB.NET 2008 (Express) (although I do have a trial (full) version of VS 2008 that I can use for debugging)

Thanks in advance.

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #4 on: July 01, 2008, 07:04:18 AM »
What is the EXACT spelling of the block 'drawing border'?

Also, don't open things for Write unless you are actually going to modify the object and even then, open for write for the shortest time possible.

vegbruiser

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #5 on: July 01, 2008, 07:19:08 AM »
Quote
The exact spelling is "Drawing Border"  :whistle: , that's one reason why it may not be working correctly.

Also, the open for write bit is a mistake as I only want to read the attributes and bung them into an XML "reporting" file.

I'll add a (couple of) Ucase entries to my code and see what happens.

Actually, I have just changed this: -

Code: [Select]
If blkobj.IsDynamicBlock = False then
to this: -

Code: [Select]
If br.IsDynamicBlock = False then
and it finds the correct block no problem

Cheers.

What it doesn't do however is find the same block in multiple layouts?

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #6 on: July 01, 2008, 07:31:31 AM »
You do understand the difference between a BlockTableRecord (Block Definition) and a BlockReference (INSERT)?

vegbruiser

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #7 on: July 01, 2008, 07:58:23 AM »
I do, yes. (at least I think I do): -

A Blocktablerecord  (Block Definition) is the block as initially defined (or subsequently updated) and the blockreference is the unique copy of that definition contained in either Modelspace or paperspace. (which then has unique attribute references)

So what you're saying is that I should be looking for Blockreferences, not blocktablerecords? I thought that's what the line: -

Code: [Select]
If ent.GetRXClass().Name.ToString() = "AcDbBlockReference" Then
was for (or have I missed the point of what you were saying entirely)?

:oops:

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #8 on: July 01, 2008, 08:08:59 AM »
Correct - you should be looking for BlockReferences.

The block definition is the master blueprint/look of all the BlockReferences that 'reference' the definition - hence all references will look exactly the same (excluding rotation/scale/attrib values), so they are NOT a unique copy by any means.

As far as this goes:
Code: [Select]
If ent.GetRXClass().Name.ToString() = "AcDbBlockReference" Then

you're correct, however it's in a loop over paperspace ONLY. If you want to get ALL references of your block def, then either:

A. 'Loop the layouts'. Essentailly do what you do now, except put an outer loop to loop the layouts, or
B. Look up GetBlockReferenceIds (this is the fastest way and the way I would do it)

vegbruiser

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #9 on: July 01, 2008, 09:27:26 AM »
Correct - you should be looking for BlockReferences.

The block definition is the master blueprint/look of all the BlockReferences that 'reference' the definition - hence all references will look exactly the same (excluding rotation/scale/attrib values), so they are NOT a unique copy by any means.

As far as this goes:
Code: [Select]
If ent.GetRXClass().Name.ToString() = "AcDbBlockReference" Then

you're correct, however it's in a loop over paperspace ONLY. If you want to get ALL references of your block def, then either:

A. 'Loop the layouts'. Essentailly do what you do now, except put an outer loop to loop the layouts, or
B. Look up GetBlockReferenceIds (this is the fastest way and the way I would do it)

Ok, I shall give that a try - I think there are some examples of how to use GetBlockReferenceIds in Jerry's book. Thanks for your help Glenn.

:)

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #10 on: July 01, 2008, 09:45:54 AM »
No probs veg. I wouldn't be following Jerry's book too closely though.

vegbruiser

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #11 on: July 02, 2008, 08:12:27 AM »
Right, I figured it was worth posting an update seeing as I've managed to cobble something together that seems to work (using Jerry's book as a guide, and some code I found here - and then converted to VB .Net): -

Code: [Select]
<CommandMethod("FindBlockInLayout")> _
        Public Sub FindBlockInLayout()
            Dim myDB As DatabaseServices.Database
            Dim myDWG As ApplicationServices.Document
            Dim myEd As EditorInput.Editor
            Dim myBlockRef As DatabaseServices.BlockReference
            Dim myTransMan As DatabaseServices.TransactionManager
            Dim myTrans As DatabaseServices.Transaction
            Dim I As Integer
            Dim J As Integer
            Dim myBT As DatabaseServices.BlockTable
            Dim myBTR As DatabaseServices.BlockTableRecord
            Dim myBTR2 As DatabaseServices.BlockTableRecord
            Dim myBTE As IEnumerator
            Dim MyBlockstoCheckIds As New DatabaseServices.ObjectIdCollection
            Dim myOtherIDs As New DatabaseServices.ObjectIdCollection
            Dim tmpObjIDs As New DatabaseServices.ObjectIdCollection
            Dim myLayoutIDs As New DatabaseServices.ObjectIdCollection
            myDWG = ApplicationServices.Application.DocumentManager.MdiActiveDocument
            myDB = myDWG.Database
            myEd = myDWG.Editor
            myTransMan = myDB.TransactionManager
            myTrans = myTransMan.StartTransaction
            myBT = DirectCast(myTrans.GetObject(myDB.BlockTableId, OpenMode.ForRead), BlockTable)
            myBTE = myBT.GetEnumerator
            While myBTE.MoveNext = True
                myBTR = DirectCast(myTrans.GetObject(myBTE.Current, OpenMode.ForRead), BlockTableRecord)
                ' Debug.Print(myBTR.Name)
                Select Case myBTR.Name.ToUpper
                    Case "DRAWING BORDER"
                        tmpObjIDs = myBTR.GetBlockReferenceIds(False, False)
                        For I = 1 To tmpObjIDs.Count
                            myBlockRef = DirectCast(myTrans.GetObject(tmpObjIDs(I - 1), OpenMode.ForRead), BlockReference)
                            MyBlockstoCheckIds.Add(myBlockRef.ObjectId)
                        Next
                    Case Else
                        If myBTR.IsAnonymous Then
                            tmpObjIDs = myBTR.GetBlockReferenceIds(False, False)
                            For I = 1 To tmpObjIDs.Count
                                myBlockRef = DirectCast(myTrans.GetObject(tmpObjIDs(I - 1), OpenMode.ForRead), BlockReference)
                                myBTR2 = DirectCast(myTrans.GetObject(myBlockRef.DynamicBlockTableRecord, OpenMode.ForRead), BlockTableRecord)
                                Select Case myBTR2.Name.ToUpper
                                    Case "DRAWING BORDER"
                                        MyBlockstoCheckIds.Add(myBlockRef.ObjectId)
                                End Select
                            Next
                        End If
                End Select
            End While
            myBT = DirectCast(myTrans.GetObject(myDB.BlockTableId, OpenMode.ForRead), BlockTable)
            myBTE = myBT.GetEnumerator
            While myBTE.MoveNext
                myBTR = DirectCast(myTrans.GetObject(myBTE.Current, OpenMode.ForRead), BlockTableRecord)
                If myBTR.IsLayout Then
                    If Not myBTR.Name.ToUpper Like "*MODEL*" Then 'should provide us with the paperspace layouts only
                        myLayoutIDs.Add(myBTR.ObjectId)
                    End If
                End If
            End While

            For I = 1 To MyBlockstoCheckIds.Count
                myBlockRef = DirectCast(myTrans.GetObject(MyBlockstoCheckIds(I - 1), OpenMode.ForRead), BlockReference)
                For J = 1 To myLayoutIDs.Count
                    Dim mylayoutBtr As BlockTableRecord = DirectCast(myTrans.GetObject(myLayoutIDs(J - 1), OpenMode.ForRead), BlockTableRecord)
                    Dim LayoutName As String = ""
                    If myBlockRef.OwnerId = mylayoutBtr.ObjectId Then
                        myBTR = DirectCast(myTrans.GetObject(myBlockRef.BlockTableRecord, OpenMode.ForRead), BlockTableRecord)
                        Dim layoutdict As DBDictionary = DirectCast(myTrans.GetObject(myDB.LayoutDictionaryId, OpenMode.ForRead), DBDictionary)
                        Dim iLayout As Int32 = 0
                        For Each id As DictionaryEntry In layoutdict
                            Dim ltr As Layout = DirectCast(myTrans.GetObject(DirectCast(id.Value, ObjectId), OpenMode.ForRead), Layout)
                            If mylayoutBtr.ObjectId = ltr.BlockTableRecordId Then
                                LayoutName = ltr.LayoutName
                                Debug.Print(ltr.LayoutName)
                            End If
                            ed.WriteMessage("" & Chr(10) & "Layout N{0} = {1}", System.Threading.Interlocked.Increment(iLayout), ltr.LayoutName)
                        Next
                        MessageBox.Show("We found a drawing border on a layout!" & vbCr & LayoutName & " Was where we found it!")
                    End If
                Next
            Next

            myTrans.Dispose()
            myTransMan.Dispose()

        End Sub

The next logical step (for each "Drawing Border" block at least) would be to iterate through it's attributereferences, and check their values against some known values (things like site number, issue date, revision etc.)

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #12 on: July 02, 2008, 08:23:40 AM »
Why are you using an IEnumerator? There are few places you need to do this and this isn't one of them.

If you looping the BlockTable, use a for each like so:

Code: [Select]
foreach (ObjectId btrId in bt)
{
  BlockTableRecord btr = trans.GetObject(btrId, OpenMode.ForRead, false) as BlockTableRecord;
  // ... do more mojo here
}

That's just typed in the edit post section, so don't take it as gospel.

vegbruiser

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #13 on: July 02, 2008, 09:12:12 AM »
Why are you using an IEnumerator? There are few places you need to do this and this isn't one of them.

If you looping the BlockTable, use a for each like so:

Code: [Select]
foreach (ObjectId btrId in bt)
{
  BlockTableRecord btr = trans.GetObject(btrId, OpenMode.ForRead, false) as BlockTableRecord;
  // ... do more mojo here
}

That's just typed in the edit post section, so don't take it as gospel.
Looking back at some other similar code, that was the way I had already done it, I did indeed just copy+paste the code that Jerry provided. Ah well.
« Last Edit: July 02, 2008, 09:25:15 AM by vegbruiser »

Glenn R

  • Guest
Re: Layouts, Blocks, Attributes...
« Reply #14 on: July 03, 2008, 07:20:01 AM »
An example for your indigestion :) :

Code: [Select]
[CommandMethod("BlocksInLayouts")]
static public void BlocksInLayoutsCommand()
{
Document doc = acadApp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

using (Transaction tr = db.TransactionManager.StartTransaction())
{
DBDictionary layoutDict = tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead, false) as DBDictionary;
if (layoutDict == null)
{
ed.WriteMessage("{0}Error: Failed to get Layout dictionary.", Environment.NewLine);
return;
}

foreach (DictionaryEntry dictEntry in layoutDict)
{
ed.WriteMessage("{0}Layout name: {1}", Environment.NewLine, dictEntry.Key);
ObjectId layoutId = (ObjectId)dictEntry.Value;

Layout layout = tr.GetObject(layoutId, OpenMode.ForRead, false) as Layout;
}

BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead, false) as BlockTable;

foreach (ObjectId btrId in bt)
{
BlockTableRecord btr = tr.GetObject(btrId, OpenMode.ForRead, false) as BlockTableRecord;

if (btr.IsLayout || btr.IsFromExternalReference || btr.IsDynamicBlock
|| btr.IsFromOverlayReference || !btr.HasAttributeDefinitions)
{

continue;
}

if (btr.Name.ToUpper() != "TESTBLOCK")
continue;

ObjectIdCollection blkRefIds = btr.GetBlockReferenceIds(true, false);
if (blkRefIds == null || blkRefIds.Count == 0)
break;

int counter = 0;
foreach (ObjectId blkRefId in blkRefIds)
{
BlockReference blkRef = tr.GetObject(blkRefId, OpenMode.ForRead, false) as BlockReference;
AttributeCollection attRefIds = blkRef.AttributeCollection;

foreach (ObjectId attRefId in attRefIds)
{
AttributeReference attRef = tr.GetObject(attRefId, OpenMode.ForRead, false) as AttributeReference;
switch (attRef.Tag)
{
case "ATTRIBUTE_A":
attRef.UpgradeOpen();
string update = string.Format("{0}. Hey there vege B!", ++counter);
attRef.TextString = update;
                                                        attRef.DowngradeOpen();
break;
default:
break;
}
}
}

break;
}

tr.Commit();
}
}

The layout stuff at the beginning doesn't need to be in there to accomplish what you were after, however I left it in there for illustrative purposes.
Have at it.

Cheers,
Glenn.
« Last Edit: July 03, 2008, 10:28:45 AM by Glenn R »