excel vba update title blocks in drawing with multiple layouts

excel vba update title blocks in drawing with multiple layouts

Anonymous
Not applicable
6,961 Views
32 Replies
Message 1 of 33

excel vba update title blocks in drawing with multiple layouts

Anonymous
Not applicable

Hi I am looking for some help on how to update the title block in drawings with multiple layout tabs. So far my code will only update the title block in the active layout. I was looking into using acSelectionsetall but couldn't figure out how to apply it. 

 


'***********************************************************************

Sub ModifyFrameBlock(DWG As AcadDocument, QUANTITY As String)

Dim blk As AcadBlockReference
Dim attr As Variant
Set blk = FindFrameBlock(DWG)

If Not blk Is Nothing Then

For Each attr In blk.GetAttributes()

If attr.TagString = "RELEASE-NO" Then

attr.TextString = RelNo

DWG.SummaryInfo.SetCustomByKey "Frame No", lbFrameName ' drawing property field

DWG.SummaryInfo.SetCustomByKey "Frame Qty", QUANTITY ' drawing property field
 End If

If attr.TagString = "RELEASE-DATE" Then
attr.TextString = TBReleaseDate
End If

Next
End If

CreatePDF DWG
DWG.AuditInfo FixError ' audit and fix error in drawing
DWG.PurgeAll ' purge drawing

DWG.Close True

End Sub

 

 

'***********************************************************

Function FindFrameBlock(DWG As AcadDocument) As AcadBlockReference
Dim ent As AcadEntity

For Each ent In DWG.PaperSpace
If TypeOf ent Is AcadBlockReference Then
If ent.Name = "blockname" Then

Set FindFrameBlock = ent
Exit Function
End If
End If
Next
Set FindFrameBlock = Nothing
End Function

'***********************************************************

0 Likes
Accepted solutions (2)
6,962 Views
32 Replies
Replies (32)
Message 2 of 33

norman.yuan
Mentor
Mentor

Well, since you search the block in AcadDocument.PaperSpace, thus, you only search the active layout. Furthermore, the the active layout is ModelSpace, the code will not get anything back. There is 2 way to do:

 

1. Set each layout to active, then search the PaperSpace, as your code to. This would be slow, because AutoCAD has to change active layout, which, not to mention, may trigger "regen", making thing even slower;

 

2. You can simply loop through all layout and search AcadLayout.Block.

 

A slight logic optimization to your code is to let FindFrameBlock() method return an array of block references, rather than one, because it is possible you may have the same block on all paper layouts. So, here is the code to do it:

 

Option Explicit

Public Sub ModifyFrameBlocks()

    Dim blkName As String
    blkName = "TBox"
    Dim i As Integer
    Dim blk As AcadBlockReference
    
    Dim blks As Variant
    blks = FindBlocksOnAllLayouts(blkName)
    
    If VarType(blks) = vbEmpty Then
        MsgBox "No block """ & blkName & """ found in current drawing!"
    Else
        For i = 0 To UBound(blks)
            Set blk = blks(i)
            ''Update each block reference here
        Next
    End If

End Sub

Private Function FindBlocksOnAllLayouts(blkName As String) As Variant

    Dim blks() As AcadBlockReference
    Dim i As Integer
    Dim ent As AcadEntity
    Dim blk As AcadBlockReference
    
    Dim lay As AcadLayout
    For Each lay In ThisDrawing.Layouts
        ''You may or may not wna to exclude "Model" layout
        '' depending on your business requirements
        If UCase(lay.Name) <> "MODEL" Then
            For Each ent In lay.Block
                If TypeOf ent Is AcadBlockReference Then
                    Set blk = ent
                    If UCase(blk.Name) = UCase(blkName) Then
                        ReDim Preserve blks(i)
                        Set blks(i) = blk
                        i = i + 1
                    End If
                End If
            Next
        End If
    Next
    
    FindBlocksOnAllLayouts = blks

End Function

Of cause, if you only need to find one block, then you can stop the searching loop once a block reference is found.

 

Another thing to optimize your code in method ModifyFrameBlock():

 

Since this method gets a AcadDocument passed in, you'd better not close the document inside this method. Rather, the document should be closed outside the method. Something like:

 

For Each dwgName in DwgNames

 

    ''Open the drawing

    Set dwg=AcadApp.Documents.Open(dwgName,...)

 

   '' Do the work

    Call ModifyFrameBlock(dwg, quantity)

 

    ''Close, save if necessary

    dwg.Close True

 

Next

 

HTH

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 33

Anonymous
Not applicable

 I am pretty new to coding, your patient is appreciated. I copied your code and here is what I have but am unable to get it to work. Please let me know what I am missing. Thanks

 

Set DWG = AcadApplication.Documents.Open(Filename)
Call ModifyFrameBlocks(DWG, QntyNum)

dwg.Close True

 

 

Public Sub ModifyFrameBlocks()

Dim blkName As String

Dim i As Integer
Dim blk As AcadBlockReference
blkName = "11x17_Frame_TB-2015" 'block name
Dim blks As Variant
blks = FindBlocksOnAllLayouts(blkName)

If VarType(blks) = vbEmpty Then
MsgBox "No block """ & blkName & """ found in current drawing!"
Else
For i = 0 To UBound(blks)
Set blk = blks(i)
''Update each block reference here
If attr.TagString = "RELEASE-NO" Then
attr.TextString = "Release1"
End If

If attr.TagString = "RELEASE-DATE" Then
attr.TextString = "yyyy/mm/dd"
End If
Next
End If
End Sub

Private Function FindBlocksOnAllLayouts(blkName As String) As Variant

Dim blks() As AcadBlockReference
Dim i As Integer
Dim ent As AcadEntity
Dim blk As AcadBlockReference

Dim lay As AcadLayout
For Each lay In DWG.Layouts
''You may or may not wna to exclude "Model" layout
'' depending on your business requirements
If UCase(lay.Name) <> "MODEL" Then
For Each ent In lay.block
If TypeOf ent Is AcadBlockReference Then
Set blk = ent
If UCase(blk.Name) = UCase(blkName) Then
ReDim Preserve blks(i)
Set blks(i) = blk
i = i + 1
End If
End If
Next
End If
Next
FindBlocksOnAllLayouts = blks
End Function

0 Likes
Message 4 of 33

Anonymous
Not applicable

what about AcSelectionSetAll? Instead of cycling through all the layouts use AcSelectionSetAll but I can not any sample codes for my condition. Appreciate your help.

0 Likes
Message 5 of 33

norman.yuan
Mentor
Mentor

When post your code, please use "insert code" button in the message window's toolbar, so that the code can be easier for viewing.

 

You also should indicate where line of code does not work (if you ever stepped through the code in debugging mode, especially when the code does not work).

 

Anyway, it is here in your code that is wrong:

 

For i = 0 To UBound(blks)
  Set blk = blks(i)
  ''Update each block reference here
  If attr.TagString = "RELEASE-NO" Then
    attr.TextString = "Release1"
  End If

  If attr.TagString = "RELEASE-DATE" Then
    attr.TextString = "yyyy/mm/dd"
  End If
Next
...

If should be:

 

For i = 0 To UBound(blks)


  Set blk = blks(i)

 

  ''Update each block reference here

  attrs=blk.GetAttributes()

  For Each attr in attrs


    If attr.TagString = "RELEASE-NO" Then
      attr.TextString = "Release1"
    End If

    If attr.TagString = "RELEASE-DATE" Then
      attr.TextString = "yyyy/mm/dd"
    End If


  Next

 

Next

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 6 of 33

norman.yuan
Mentor
Mentor

Yes, you can create a selectionset with or without filter to locate the target block references. In general, it does not make much/noticeable difference in term of speed of finding the blocks. in comparison to the way of looping through AcadLayout.Block.

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 7 of 33

Anonymous
Not applicable

I am getting a ByRef argument type mismatch for (blkName). What am I doing wrong now. Also, is it correct to pass DWG to the

ModifyFrameBlock and FindBlocksOnAllLayouts?
 
 

 

Dim DWG As AcadDocument 
Dim releaseFile As String

Set DWG = ACAD.Documents.Open(releaseFile) ModifyFrameBlock DWG Public Sub ModifyFrameBlock(DWG As AcadDocument) Dim blkName As String blkName = "11x17_Frame_TB-2015" Dim i As Integer Dim blk As AcadBlockReference Dim blks As Variant blks = FindBlocksOnAllLayouts(blkName) 'I am getting a ByRef argument type mismatch for (blkName) If VarType(blks) = vbEmpty Then MsgBox "No block """ & blkName & """ found in current drawing!" Else For i = 0 To UBound(blks) Set blk = blks(i) attrs = blk.GetAttributes() For Each attr In attrs If attr.TagString = "RELEASE-NO" Then attr.TextString = "Release1" End If If attr.TagString = "RELEASE-DATE" Then attr.TextString = "yyyy/mm/dd" End If Next Next End If End Sub Private Function FindBlocksOnAllLayouts(DWG As AcadDocument, blkName As String) Dim blks() As AcadBlockReference Dim i As Integer Dim ent As AcadEntity Dim blk As AcadBlockReference Dim lay As AcadLayout For Each lay In DWG.Layouts If UCase(lay.Name) <> "MODEL" Then For Each ent In lay.block If TypeOf ent Is AcadBlockReference Then Set blk = ent If UCase(blk.Name) = UCase(blkName) Then ReDim Preserve blks(i) Set blks(i) = blk i = i + 1 End If End If Next End If Next FindBlocksOnAllLayouts = blks End Function
0 Likes
Message 8 of 33

norman.yuan
Mentor
Mentor
Accepted solution

@Anonymous wrote:

I am getting a ByRef argument type mismatch for (blkName). What am I doing wrong now. Also, is it correct to pass DWG to the

ModifyFrameBlock and FindBlocksOnAllLayouts

Dim DWG As AcadDocument
Dim releaseFile As String

Set DWG = ACAD.Documents.Open(releaseFile) ModifyFrameBlock DWG Public Sub ModifyFrameBlock(DWG As AcadDocument) Dim blkName As String blkName = "11x17_Frame_TB-2015" Dim i As Integer Dim blk As AcadBlockReference Dim blks As Variant blks = FindBlocksOnAllLayouts(blkName) 'I am getting a ByRef argument type mismatch for (blkName) If VarType(blks) = vbEmpty Then .... End If End Sub Private Function FindBlocksOnAllLayouts(DWG As AcadDocument, blkName As String) .... End Function

Well, I guess you maybe worked "too hard" to see the obvious. Take some time off in the weekend :-(.

 

The function FindBlocksOnAllLayouts() has 2 argument, but your code only supplys one. That line of code should be:

 

blks = FindBlocksOnAllLayouts(DWG, blkName)

Norman Yuan

Drive CAD With Code

EESignature

Message 9 of 33

Anonymous
Not applicable
Accepted solution

as per your function signature:

Function FindBlocksOnAllLayouts(DWG As AcadDocument, blkName As String)

you must call it by supplying both "AcadDocument" and "String" type arguments:

 

blks = FindBlocksOnAllLayouts(DWG, blkName)

furthermore I'd suggest you to

 

- use Option Explicit at the very top of each module.

  this will force you to explicitly declare all used variables, instead of assuming undeclared ones as of "Variant" type", but this effort will give you much more control over your code

 so you may want to add/revise the following:

Dim blks() As AcadBlockReference '<<< declare it as of the proper type
Dim attrs As Variant '<<< declare it!
Dim attr As AcadAttribute '<<< declare it!

 

- in FindBlocksOnAllLayouts(), add a check on found block having attributes: 

 

                    If UCase(blk.Name) = UCase(blkName) Then
                        If blk.HasAttributes Then '<<< check if block has attributes before adding it to the array
                            ReDim Preserve blks(i)
                            Set blks(i) = blk
                            i = i + 1
                        End If
                    End If

  so as to fill blks() with relevant block references only

 

- add some exception handling in case no block is found

 

 

Finally you could consider the following alternative "SelectionSet" approach:

 

 

Public Sub ModifyFrameBlock(DWG As AcadDocument)
    
    Dim blkName As String
    blkName = "11x17_Frame_TB-2015"
    Dim i As Integer
    Dim blk As AcadBlockReference
    Dim blks() As AcadBlockReference  '<<< declare it as of the proper type
    Dim attrs As Variant '<<< declare it!
    Dim attr As AcadAttribute '<<< declare it!

    Dim blcksObj As AcadSelectionSet
    
    Set blcksObj = FindBlocksOnAllLayouts2(DWG, blkName) '<<< collect all blocks with given name in a selectionset

    If blcksObj.Count > 0 Then ' if any block found
        ReDim Preserve blks(0 To blcksObj.Count)
        For Each blk In blcksObj
            If blk.HasAttributes Then '<<< check if block has attributes before adding it to the array
                Set blks(i) = blk
                i = i + 1
            End If
        Next
        
        If i = 0 Then
            MsgBox "No blocks """ & blkName & """ with attributes found in current drawing!"
        Else
            ReDim Preserve blks(0 To i - 1)
            For i = 0 To UBound(blks)
                Set blk = blks(i)
                attrs = blk.GetAttributes()
                For Each attr In attrs
                    If attr.TagString = "RELEASE-NO" Then
                        attr.TextString = "Release1"
                    ElseIf attr.TagString = "RELEASE-DATE" Then
                        attr.TextString = "yyyy/mm/dd"
                    End If
                Next
            Next
        End If
    Else
        msgBox "No blocks """ & blkName & """ found in current drawing!"
    End If
End Sub


Function FindBlocksOnAllLayouts2(DWG As AcadDocument, blkName As String) As AcadSelectionSet
    Set FindBlocksOnAllLayouts2 = CreateSelectionSet(DWG, "blcks")
    
    Dim gpCode(0 To 1) As Integer
    Dim dataValue(0 To 1) As Variant
    gpCode(0) = 0:    dataValue(0) = "INSERT"
    gpCode(1) = 2:    dataValue(1) = blkName
    FindBlocksOnAllLayouts2.Select acSelectionSetAll, , , gpCode, dataValue
End Function


Function CreateSelectionSet(acDoc As AcadDocument, SSset As String) As AcadSelectionSet
    On Error Resume Next
    Set CreateSelectionSet = acDoc.SelectionSets(SSset)
    If Err Then Set CreateSelectionSet = acDoc.SelectionSets.Add(SSset)
    CreateSelectionSet.Clear
End Function

 

Message 10 of 33

robert.kulas
Contributor
Contributor

Hello,

I would like to implement this code to my excel application, but the compiler sends me error info: "Object required" and marked the line (please see picture below)

robertkulas_0-1674141980574.png

Could you help me find what is the issue?

I would appreciate for help.

0 Likes
Message 11 of 33

Ed__Jobe
Mentor
Mentor

It looks like the variable "attr" is not dimensioned anywhere.

Dim attr As AcadAttribute

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes
Message 12 of 33

norman.yuan
Mentor
Mentor

AcadBlockReference returns  a Variant, which is an array of AcadAttributeReference object. In VBA, For Each... Next does not apply to array, thus your code error out at:

 

For Each attr in attrs

...

Next

 

the code should be

Dim i as Integer

For i=0 to UBound(attrs)

  attr=attrs(i)

  If attr.TagString = "xxxx" Then

    ... ...

  End If

Next

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 13 of 33

robert.kulas
Contributor
Contributor

Unfortunately, this changes did not solve the problem. Below is the full code from the post with minor changes.

 

Sub ModifyFrameBlock()

 

Dim AutoCadApp As Object
Dim DWG As AcadDocument

Dim blkName As String
blkName = "DwgTable01"
Dim i As Integer
Dim blk As AcadBlockReference
Dim blks() As AcadBlockReference '<<< declare it as of the proper type
Dim attrs As Variant '<<< declare it!
Dim attr As AcadAttribute '<<< declare it!

Dim blcksObj As AcadSelectionSet

 

Set AutoCadApp = GetObject(, "AutoCAD.Application")
Set DWG = AutoCadApp.ActiveDocument

Set blcksObj = FindBlocksOnAllLayouts2(DWG, blkName) '<<< collect all blocks with given name in a selectionset

 

If blcksObj.Count > 0 Then ' if any block found

 

ReDim Preserve blks(0 To blcksObj.Count)

 

For Each blk In blcksObj
If blk.HasAttributes Then '<<< check if block has attributes before adding it to the array
Set blks(i) = blk
i = i + 1
End If
Next

 

If i = 0 Then
MsgBox "No blocks """ & blkName & """ with attributes found in current drawing!"
Else
ReDim Preserve blks(0 To i - 1)
For i = 0 To UBound(blks)
Set blk = blks(i)
attrs = blk.GetAttributes()
For Each attr In attrs
If attr.TagString = "REVISION" Then
attr.TextString = "Rev 1"
ElseIf attr.TagString = "SHEET" Then
attr.TextString = "1/100"
End If
Next
Next
End If
Else
MsgBox "No blocks """ & blkName & """ found in current drawing!"
End If
End Sub

 

0 Likes
Message 14 of 33

norman.yuan
Mentor
Mentor

Firstly, please use the "</>" button of the toolbar above the message window to post code, which preserves the code format so that the code is easy to read.

 

Your previous code did not indicates how the variable is declared. Now, seeing the whole section of the code, here is at least 3 eerrors:

 

1. as I previously replied, you cannot use For Each... against an array

For Each attr in attrs

   ...

Next

 

Has to be 

For i=0 to UBound(attrs)

 ...

Next

 

2. the variable "attr" is declared wrong: it should be AcadAttributeReference, not AcadAttribute

 

3. Since "attr" is an Object, you need to use "Set" to assign value to it

 

For i=....

  Set attr=attrs(i)

Next

 

 

Norman Yuan

Drive CAD With Code

EESignature

Message 15 of 33

robert.kulas
Contributor
Contributor

thank you for the answer. I do not know what is wrong (maybe my fault). I changed acc. to your message but it does not work...

Could you write some code?

I would appreciate for it.

0 Likes
Message 16 of 33

robert.kulas
Contributor
Contributor

Super, thank you again. I have found the issue. I had to add additional control variable (j)...

0 Likes
Message 17 of 33

norman.yuan
Mentor
Mentor

I'd strongly suggest you do wrap your code into smaller chunk, which not only makes the code much easier to follow/read/understand, but also naturally prevents the bug (of unknowingly used a variable with undesired initial value). Something like:

Private Sub SetAttributesInBlocks(blks As Variant)
  Dim i As Integer
  Dim blk As AcadBlockReference
  
  For i = 0 To UBound(blks)
   SetAttributes blk
  Next
End Sub

Private Sub SetAttributes(blk As AcadBlockReference)
  Dim i As Integer
  Dim attrs As Variant
  Dim attr As AcadAttributeReference
  If Not blk.HasAttributes Then Exit Sub
  attrs=blk.GetAttributes()
  For i = 0 To UBound(attrs)
    Set attr=attrs(i)
    Select Case UCase(attr.TagString)
      Case "REVISION"
        attr.TextString="xxxxx"
      Case "SHEET"
        attr.TextString="yyyyy"
    End Select
  Next
  
End Sub

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 18 of 33

Ed__Jobe
Mentor
Mentor

@robert.kulas 

In addition, I would also suggest that you set your VBA Options to require variable declaration. This automatically inserts "Option Explicit" in the Declarations section of any new module you create. This will cause the IDE to notify you if you try to use a variable that hasn't been dimensioned. For existing modules, you will have to manually add the line to the top  of your modules.

acad vba options.png

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes
Message 19 of 33

robert.kulas
Contributor
Contributor

Thank you for all your support.

0 Likes
Message 20 of 33

Ed__Jobe
Mentor
Mentor

If your problem is solved, please mark mark the posts accepted as solution.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes