Author Topic: Multi Layout Title Block Updater  (Read 2143 times)

0 Members and 1 Guest are viewing this topic.

trembre

  • Guest
Multi Layout Title Block Updater
« on: October 13, 2011, 06:20:22 AM »
I'm sure many Swampers(?) will be familiar with the Update lisp routine that reads attributes in from CSV files to (usually) titleblocks? See below for my version of this written in VB, with the extended functionality of it being able to update multi-layout drawing files and target a specific block by name.  The majority of the layout iteration is taken from http://dwgdotnet.blogspot.com/2008/07/finding-all-title-blocks-on-all-layouts.html - can't remember where I got the CSV reader from...

For this to work, you mostly need the same pre-requisites as the lisp version:
1. A txt file named RegisterPointer.txt in the same folder as the dwg file, this contains a single line with the full name (directory path, filename and extension) of the CSV.
2. A CSV file containing your titleblock info with specific layout names in column B, with attribute tags being defined in the first row
3. A txt file named "Title Text Block" in the same folder as your CSV file with the block name you wish to update.  This file contains a single line with the block name to be updated.

To re-confirm - this works off individual layout names listed in the CSV's column B and is independent of the drawing name.

This is provided "as is", feel free to use/improve/constructively criticize! 

Code: [Select]
  <CommandMethod("Update")> _
    Public Sub Update()
        ' Get the current document database and active drawing
        Dim oDb As Database = HostApplicationServices.WorkingDatabase
        Dim oDwg As Autodesk.AutoCAD.ApplicationServices.Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
        Dim oEd As Editor = oDwg.Editor
        Dim ed As Autodesk.AutoCAD.EditorInput.Editor = oDwg.Editor
        Dim strThisDrawing As String = oDwg.Name
        Dim strThisDrawingNoExt As String = Path.GetFileNameWithoutExtension(strThisDrawing)
        Dim strThisPath As String = Path.GetDirectoryName(strThisDrawing)
        Dim strPointerFile As String = strThisPath + "\RegisterPointer.txt"
        Dim dstRegisterInfo As DataSet = Nothing
        Dim xlsArrayList As ArrayList = Nothing
        Dim dstSettingsInfo As DataSet = Nothing
        Dim lstAttributeTags As New ArrayList
        Dim lstAttributeTexts As New ArrayList
        Dim strLayoutName As String
        Dim intAttCount As String = 0
        Dim strTextBlock As String = Nothing
        Dim strPointedFile As String
        Dim blnSkipLayout As Boolean ' this value is toggled depending on whether there is any entry in the register for the active layout
        blnSkipLayout = True

        If File.Exists(strPointerFile) = True Then
            Dim objStreamReader As New StreamReader(strPointerFile)
            strPointedFile = objStreamReader.ReadLine
            dstRegisterInfo = ReadFromCSV(strPointedFile) ' grab the information from the Excel register
            Try
                strTextBlock = GetTitleText(strPointedFile) ' grab the title text block name from the txt file in the same folder as the register
            Catch
                oEd.WriteMessage(Environment.NewLine & "No Title Text Block txt File Detected")
                Exit Sub
            End Try
        Else
            oEd.WriteMessage(Environment.NewLine & "No Pointer File Detected")
            Exit Sub
        End If

        For Each dt As System.Data.DataTable In dstRegisterInfo.Tables
            For Each dc As System.Data.DataColumn In dt.Columns
                lstAttributeTags.Add(dc.ColumnName)
            Next
        Next

        ' Start a transaction
        Dim oTrans As Transaction = oDb.TransactionManager.StartTransaction

        ' Get the current layout id and layout name
        Dim curLayoutID As ObjectId = LayoutManager.Current.GetLayoutId(LayoutManager.Current.CurrentLayout)
        Dim curLayout As Layout = CType(curLayoutID.GetObject(OpenMode.ForRead), Layout)

        ' Create an arraylist to store our layout entity ids in
        Dim oTblocks As New ArrayList

        Try            ' Lock the drawing
            Using locked As Autodesk.AutoCAD.ApplicationServices.DocumentLock = oDwg.LockDocument()
                ' Get the drawings layout dictionary
                Dim layoutDict As DBDictionary = CType(oTrans.GetObject(oDb.LayoutDictionaryId, OpenMode.ForRead), DBDictionary)
                Dim indexTb As Integer = 1
                For Each id As DictionaryEntry In layoutDict ' iterate through all the drawing layouts

                    strLayoutName = id.Key.ToString ' the id.key is the layout name
                    If Not strLayoutName = "Model" Then ' Ignore modelspace

                        blnSkipLayout = True

                        lstAttributeTexts.Clear() ' clear array from possible previous run
                        For Each dt As System.Data.DataTable In dstRegisterInfo.Tables ' populate an array with the relevant attribute textstrings, depending on the current layout
                            For Each dr As System.Data.DataRow In dt.Rows
                                If Not dr(1).ToString = Nothing Then
                                    If dr(1).ToString = strLayoutName Then
                                        blnSkipLayout = False
                                        For Each item In dr.ItemArray
                                            lstAttributeTexts.Add(item.ToString)
                                        Next
                                    End If
                                End If
                            Next
                        Next

                        If blnSkipLayout = False Then
                            Dim oLayout As Layout = CType(oTrans.GetObject(CType(id.Value, ObjectId), OpenMode.ForRead), Layout) ' use the layouts ID to grab the layout
                            Dim oBtr As BlockTableRecord = CType(oTrans.GetObject(oLayout.BlockTableRecordId, OpenMode.ForRead, False), BlockTableRecord) ' get the layouts block table
                            Dim oBtre As BlockTableRecordEnumerator = oBtr.GetEnumerator ' get the block table's enumerator
                            While oBtre.MoveNext ' iterate through the enumerator
                                Dim oEnt As Entity = CType(oTrans.GetObject(oBtre.Current, OpenMode.ForRead), Entity)
                                If TypeOf oEnt Is BlockReference Then ' if its a block reference
                                    Dim oBr As BlockReference = CType(oEnt, BlockReference)
                                    If oBr.Name.Equals(strTextBlock) Then ' if its name matches the title text block name
                                        Dim oDoId As ObjectId = oBr.ObjectId ' get the references object id
                                        Dim attRefIds As AttributeCollection = oBr.AttributeCollection ' get its attribute collection
                                        For Each attRefID As ObjectId In attRefIds ' iterate through the attributes
                                            Dim attref As AttributeReference = CType(oTrans.GetObject(attRefID, OpenMode.ForWrite, False), AttributeReference) ' open each attribute for write
                                            For intLoop As Integer = 0 To lstAttributeTags.Count - 1 ' loop through the known number of attributes as provided by the Excel sheet
                                                If lstAttributeTags(intLoop).ToString = attref.Tag.ToString Then ' if the current attribute matches the array entry
                                                    If lstAttributeTexts.Count <= lstAttributeTags.Count Then
                                                        attref.TextString = lstAttributeTexts(intLoop).ToString ' change the textstring to the array entry
                                                    End If
                                                    intAttCount = intAttCount + 1
                                                End If
                                            Next
                                        Next
                                    End If
                                End If
                            End While
                        End If
                    End If
                Next
            End Using

            ed.Regen()
            oTrans.Commit()
        Catch ex As System.Exception
            MsgBox(ex.Message, MsgBoxStyle.Information, "Error")
        Finally
            oEd.WriteMessage(Environment.NewLine & "Update Complete. " & intAttCount.ToString & " Attributes Updated")
            oTrans.Dispose()
        End Try
    End Sub
    Private Function GetTitleText(ByVal Register As String)
        Dim strTitleTextFile As String
        Dim strTitleTextBlockName As String
        Dim strThisPath As String = Path.GetDirectoryName(Register)

        strTitleTextFile = strThisPath + "\Title Text Block.txt"
        Dim objStreamReader As New StreamReader(strTitleTextFile)
        strTitleTextBlockName = objStreamReader.ReadLine

        Return strTitleTextBlockName

    End Function

    Private Function ReadFromCSV(ByVal CSVFile As String)
        Dim strLine As String
        Dim strArray() As String
        Dim ds As DataSet = New DataSet
        Dim dt As System.Data.DataTable = ds.Tables.Add("TheData")
        Dim aFile As FileStream = New FileStream(CSVFile, FileMode.Open)
        Dim sr As StreamReader = New StreamReader(aFile)

        strLine = sr.ReadLine

        strArray = Split(strLine, ",")

        Dim x As Integer
        Dim y As Integer = strArray.GetUpperBound(0)

        For x = 0 To y
            dt.Columns.Add(strArray(x).Trim())
        Next

        strLine = sr.ReadLine

        While Not strLine = Nothing
            strArray = Split(strLine, ",")
            Dim dr As DataRow = dt.NewRow
            For c As Integer = 0 To strArray.GetUpperBound(0)
                dr(c) = strArray(c).Trim()
            Next
            dt.Rows.Add(dr)
            strLine = sr.ReadLine
        End While

        sr.Close()

        Return ds

    End Function

Jeff H

  • Needs a day job
  • Posts: 6151
Re: Multi Layout Title Block Updater
« Reply #1 on: October 13, 2011, 08:34:13 PM »
Welcome trembre and thanks for sharing!!