Is there a VB.net routine to change attributes in an individual drawing block
that is selected. I have looked at routines the change the block reference
but that changes all drawing blocks in a drawing.
Thank you,
This blog post should get you started:
http://through-the-interface.typepad.com/through_the_interface/2007/07/updating-a-spec.html
Cheers
"How we think determines what we do, and what we do determines what we get."
I'm actually currently working on just such a routine. At this time I've handled block selection, dynamic form build from the attribute prompts in the block, and at this time have it spit back out the text entered in the form textboxes. I'm working on putting together the text from the form and the value of the attributes in the block. If I get it done I'll be sure to share. Code below as it sits now. Not cleaned up obviously. If anyone has any suggestions on how to get from txtarray(z) to the attribute value in the selected block I would be very appreciative.
Imports System Imports System.IO Imports System.Collections.Specialized Imports System.Windows Imports Autodesk.AutoCAD Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.Windows Imports Autodesk.AutoCAD.Internal Imports Autodesk.AutoCAD.Interop Imports Autodesk.AutoCAD.ComponentModel Imports Autodesk.AutoCAD.GraphicsInterface Imports Autodesk.AutoCAD.GraphicsSystem Imports Autodesk.AutoCAD.LayerManager Imports Autodesk.AutoCAD.PlottingServices Imports Autodesk.AutoCAD.Publishing Public Class EditAtt Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument 'Current drawing Dim db As Database = acDoc.Database 'Current drawing database Dim ed As Editor = acDoc.Editor 'Current drawing editor Dim x As Integer = 0 Dim y As Integer = 0 Dim tbname As String = Nothing Dim tb As System.Windows.Forms.TextBox Dim lb As System.Windows.Forms.Label Public Property promptlist As New List(Of String)() Public Property taglist As New List(Of String)() <CommandMethod("ea")> _ Public Sub EditAtt() Dim form1 As EditAtt form1 = New EditAtt() Application.ShowModelessDialog(form1) form1 = Nothing End Sub Public Sub EditAtt_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim FilterList(0) As TypedValue FilterList.SetValue(New TypedValue(CInt(DxfCode.Start), "INSERT"), 0) Dim filter As New SelectionFilter(FilterList) Dim opts As New PromptSelectionOptions() Try opts.MessageForAdding = "Select a block with text to edit: " opts.SingleOnly = True Dim res As PromptSelectionResult = ed.GetSelection(opts, filter) If res.Status <> PromptStatus.OK Then Application.ShowAlertDialog("Try to select a block next time.") Exit Sub Else End If Dim selSet As SelectionSet = res.Value Dim idArray() As ObjectId = selSet.GetObjectIds Using tr As Transaction = db.TransactionManager.StartTransaction For Each blkId As ObjectId In idArray Dim blkRef As BlockReference = CType(tr.GetObject(blkId, OpenMode.ForRead), BlockReference) Dim bt As BlockTable = tr.GetObject(db.BlockTableId, OpenMode.ForRead) Dim btr As BlockTableRecord = CType(tr.GetObject(bt(blkRef.Name), OpenMode.ForRead), BlockTableRecord) If btr.HasAttributeDefinitions Then Dim blkname As String = blkRef.Name.ToString For Each ObjId As ObjectId In btr Dim obj As Entity = tr.GetObject(ObjId, OpenMode.ForRead) If TypeOf obj Is AttributeDefinition Then Dim AttDef As AttributeDefinition = obj taglist.Add(AttDef.Tag) promptlist.Add(AttDef.Prompt) Dim prmt As String = promptlist(x) tbname = "textbox" & x Dim frm1 As New System.Windows.Forms.Form lb = New System.Windows.Forms.Label With lb .Size = New System.Drawing.Size(100, 40) .Location = New System.Drawing.Point(20, x * 50 + 45) .Text = prmt End With tb = New System.Windows.Forms.TextBox With tb .Name = "tb" & x .Size = New System.Drawing.Size(500, 20) .Location = New System.Drawing.Point(120, x * 50 + 40) End With With Me .Text = blkname .SplitContainer1.Panel1.Controls.Add(lb) .SplitContainer1.Panel1.Controls.Add(tb) .AutoSize = True .Update() End With x = x + 1 End If Next End If tr.Commit() Next End Using Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(("Exception: " + ex.Message)) Finally End Try End Sub Public Sub OK_Click(sender As Object, e As EventArgs) Handles OK.Click Dim txtlist As New List(Of String)() Dim childc As System.Windows.Forms.Control For Each childc In Me.SplitContainer1.Panel1.Controls If TypeOf childc Is System.Windows.Forms.TextBox Then txtlist.Add(childc.Text.ToString) End If Next Dim txtarray As String() = txtlist.ToArray() Dim tagarray As String() = taglist.ToArray() Dim prmtarray As String() = promptlist.ToArray() For z As Integer = 0 To txtarray.Length - 1 MsgBox(txtarray(z)) MsgBox(prmtarray(z)) MsgBox(tagarray(z)) Next Me.Close() Me.Dispose() Dispose() End Sub Private Sub Cancel_Click(sender As Object, e As EventArgs) Handles Cancel.Click Me.Close() Me.Dispose() Dispose() End Sub End Class
I looked at the code in the link that blockbox suggested. I converted it to Vb.net but I am having problems with it.
I can figure out what ".Toupper" means in the code below. I know if I try to set blockName to blkname as a string
it causes and error a statement ar.UpgradeOpen(). I do not know what upgradeopen does.
Dim pr As PromptResult = ed.GetString(vbLf & "Enter name of block to search for: ") 'Dim pr As PromptResult = ed.GetString(vbLf & blkName & vbLf) 'Dim pr As PromptResult = bkname If pr.Status <> PromptStatus.OK Then Return End If Dim blockName As String = pr.StringResult.ToUpper() 'blockName = blkName (blkName as a string)
Here is my complete code in the box below. It is in form 2 with a textbox4 and a button click event.
Imports System Imports System.IO Imports System.Diagnostics Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.EditorInput Public Class Form2 Private blkName As String Private idArray As ObjectId() Private Property btrid As Database Private blockname As String Public Sub SelectForBlkName() Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor Dim db As Database = HostApplicationServices.WorkingDatabase Dim tr As Transaction = db.TransactionManager.StartTransaction() ' Start the transaction Try ' Build a filter list so that only ' block references are selected Dim filList As TypedValue() = New TypedValue(0) {New TypedValue(CInt(DxfCode.Start), "INSERT")} 'Private filList As TypedValue() = New TypedValue(0) {New TypedValue(CInt(DxfCode.Start), "INSERT")} Dim filter As New SelectionFilter(filList) 'Was inbetween Note 3 in public Shared 'Dim filList As TypedValue() = New TypedValue(0) {New TypedValue(CInt(DxfCode.Start), "INSERT")} 'Dim filter As New SelectionFilter(filList) 'was inbetween note 3 in public shared Dim opts As New PromptSelectionOptions() Dim dbObj As DBObject Me.Hide() opts.MessageForAdding = "Select block references: " Dim res As PromptSelectionResult = ed.GetSelection(opts, filter) '***Dim ent As Entity = acTrans.GetObject(acObjId, OpenMode.ForRead) ' Do nothing if selection is unsuccessful If res.Status <> PromptStatus.OK Then Return End If Dim selSet As SelectionSet = res.Value 'Dim res1 As PromptSelectionResult = ed.GetEntity 'Dim ent As Entity = tr.GetObject(acObjId, OpenMode.ForRead) idArray = selSet.GetObjectIds() For Each blkId As ObjectId In idArray Dim blkRef As BlockReference = DirectCast(tr.GetObject(blkId, OpenMode.ForRead), BlockReference) Dim btr As BlockTableRecord = DirectCast(tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead), BlockTableRecord) dbObj = tr.GetObject(blkId, OpenMode.ForRead) 'dbObj. 'TextBox4.Text = btr.Name blkName = btr.Name ed.WriteMessage(vbLf & "Block: " + btr.Name) 'MsgBox(btr.Name) 'For Each attId As ObjectId In btr 'For Each acObjId As ObjectId In acBlkTblRec Dim ent As Entity = tr.GetObject(blkId, OpenMode.ForRead) ed.WriteMessage(vbLf & "Layer: " & ent.Layer.ToString()) 'Next btr.Dispose() 'Dim attCol As AttributeCollection = blkRef.AttributeCollection 'For Each attId As ObjectId In attCol 'Dim attRef As AttributeReference = DirectCast(tr.GetObject(attId, OpenMode.ForRead), AttributeReference) 'Dim str As String = ((vbLf & " Attribute Tag: " + attRef.Tag & vbLf & " Attribute String: ") + attRef.TextString) 'ed.WriteMessage(str) 'Next Next tr.Commit() Catch ex As System.Exception 'Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(("Exception: " + ex.Message)) MsgBox("Selection Routine Exception: " + ex.Message) tr.Dispose() Exit Sub Finally tr.Dispose() End Try ' ListAttributes() 'Me.Show() 'Returnvalue = blkName End Sub Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click SelectForBlkName() UpdateAttribute() End Sub Public Sub UpdateAttribute() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor ' Have the user choose the block and attribute ' names, and the new attribute value Dim pr As PromptResult = ed.GetString(vbLf & "Enter name of block to search for: ") 'Dim pr As PromptResult = ed.GetString(vbLf & blkName & vbLf) 'Dim pr As PromptResult = bkname If pr.Status <> PromptStatus.OK Then Return End If Dim blockName As String = pr.StringResult.ToUpper() 'blockName = blkName (blkName as a string) pr = ed.GetString(vbLf & "Enter tag of attribute to update: ") If pr.Status <> PromptStatus.OK Then Return End If Dim attbName As String = pr.StringResult.ToUpper() pr = ed.GetString(vbLf & "Enter new value for attribute: ") If pr.Status <> PromptStatus.OK Then Return End If Dim attbValue As String = pr.StringResult UpdateAttributesInDatabase(db, blockName, attbName, attbValue) End Sub Private Sub UpdateAttributesInDatabase(db As Database, blockName As String, attbName As String, attbValue As String) Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim ed As Editor = doc.Editor ' Get the IDs of the spaces we want to process ' and simply call a function to process each Dim msId As ObjectId, psId As ObjectId Dim tr As Transaction = db.TransactionManager.StartTransaction() Using tr Dim bt As BlockTable = DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) msId = bt(BlockTableRecord.ModelSpace) psId = bt(BlockTableRecord.PaperSpace) ' Not needed, but quicker than aborting tr.Commit() End Using Dim msCount As Integer = UpdateAttributesInBlock(msId, blockName, attbName, attbValue) Dim psCount As Integer = UpdateAttributesInBlock(psId, blockName, attbName, attbValue) ed.Regen() ' Display the results ed.WriteMessage(vbLf & "Processing file: " & Convert.ToString(db.Filename)) ed.WriteMessage(vbLf & "Updated {0} instance{1} of " & "attribute {2} in the modelspace.", msCount, If(msCount = 1, "", "s"), attbName) ed.WriteMessage(vbLf & "Updated {0} instance{1} of " & "attribute {2} in the default paperspace.", psCount, If(psCount = 1, "", "s"), attbName) End Sub Private Function UpdateAttributesInBlock(btrId As ObjectId, blockName As String, attbName As String, attbValue As String) As Integer ' Will return the number of attributes modified Dim changedCount As Integer = 0 Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Dim ed As Editor = doc.Editor Dim tr As Transaction = doc.TransactionManager.StartTransaction() Using tr Dim btr As BlockTableRecord = DirectCast(tr.GetObject(btrId, OpenMode.ForRead), BlockTableRecord) ' Test each entity in the container... For Each entId As ObjectId In btr Dim ent As Entity = TryCast(tr.GetObject(entId, OpenMode.ForRead), Entity) If ent IsNot Nothing Then Dim br As BlockReference = TryCast(ent, BlockReference) If br IsNot Nothing Then Dim bd As BlockTableRecord = DirectCast(tr.GetObject(br.BlockTableRecord, OpenMode.ForRead), BlockTableRecord) ' ... to see whether it's a block with ' the name we're after If bd.Name.ToUpper() = blockName Then ' Check each of the attributes... For Each arId As ObjectId In br.AttributeCollection Dim obj As DBObject = tr.GetObject(arId, OpenMode.ForRead) Dim ar As AttributeReference = TryCast(obj, AttributeReference) If ar IsNot Nothing Then ' ... to see whether it has ' the tag we're after If ar.Tag.ToUpper() = attbName Then ' If so, update the value ' and increment the counter ar.UpgradeOpen() ar.TextString = attbValue ar.DowngradeOpen() changedCount += 1 End If End If Next End If ' Recurse for nested blocks changedCount += UpdateAttributesInBlock(br.BlockTableRecord, blockName, attbName, attbValue) End If End If Next tr.Commit() End Using Return changedCount End Function Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load End Sub End Class
My problem with vb.net and Autocad is the I cannot find a place that tell how the statements are defined and used.
Any suggestions? ar.UpgradeOpen() is in the Function UpdateAttributesInBlock near the ending of the code.
Thank you,
This is sloppy but currently working for me as desired. It needs some tweaking I know.
Imports System Imports System.IO Imports System.Collections.Specialized Imports System.Windows Imports Autodesk.AutoCAD Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.Geometry Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.Windows Imports Autodesk.AutoCAD.Internal Imports Autodesk.AutoCAD.Interop Imports Autodesk.AutoCAD.ComponentModel Imports Autodesk.AutoCAD.GraphicsInterface Imports Autodesk.AutoCAD.GraphicsSystem Imports Autodesk.AutoCAD.LayerManager Imports Autodesk.AutoCAD.PlottingServices Imports Autodesk.AutoCAD.Publishing Public Class EditAtt Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument 'Current drawing Dim db As Database = acDoc.Database 'Current drawing database Dim ed As Editor = acDoc.Editor 'Current drawing editor Dim x As Integer = 0 Dim y As Integer = 0 Dim tbname As String = Nothing Dim tb As System.Windows.Forms.TextBox Dim lb As System.Windows.Forms.Label Public Property promptlist As New List(Of String)() Public Property taglist As New List(Of String)() Public Property txtlist As New List(Of String)() Public Property br As BlockReference Public Property bt As BlockTable Public Property btr As BlockTableRecord <CommandMethod("ea")> _ Public Sub EditAtt() Dim form1 As EditAtt form1 = New EditAtt() Application.ShowModalDialog(form1) form1 = Nothing End Sub Public Sub EditAtt_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim FilterList(0) As TypedValue FilterList.SetValue(New TypedValue(CInt(DxfCode.Start), "INSERT"), 0) Dim filter As New SelectionFilter(FilterList) Dim opts As New PromptSelectionOptions() Try opts.MessageForAdding = "Select a block with text to edit: " opts.SingleOnly = True Dim res As PromptSelectionResult = ed.GetSelection(opts, filter) If res.Status <> PromptStatus.OK Then Application.ShowAlertDialog("Try to select a block next time.") Exit Sub Else End If Dim selSet As SelectionSet = res.Value Dim idArray() As ObjectId = selSet.GetObjectIds Using tr As Transaction = db.TransactionManager.StartTransaction For Each blkId As ObjectId In idArray br = CType(tr.GetObject(blkId, OpenMode.ForRead), BlockReference) bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) btr = CType(tr.GetObject(bt(br.Name), OpenMode.ForRead), BlockTableRecord) If btr.HasAttributeDefinitions Then Dim blkname As String = br.Name.ToString For Each ObjId As ObjectId In btr Dim obj As Entity = tr.GetObject(ObjId, OpenMode.ForRead) If TypeOf obj Is AttributeDefinition Then Dim AttDef As AttributeDefinition = obj taglist.Add(AttDef.Tag) promptlist.Add(AttDef.Prompt) Dim prmt As String = promptlist(x) tbname = "textbox" & x Dim frm1 As New System.Windows.Forms.Form lb = New System.Windows.Forms.Label With lb .Size = New System.Drawing.Size(100, 40) .Location = New System.Drawing.Point(10, x * 40 + 45) .Text = prmt End With tb = New System.Windows.Forms.TextBox With tb .Name = "tb" & x .Size = New System.Drawing.Size(500, 20) .Location = New System.Drawing.Point(120, x * 40 + 40) End With With Me .Text = blkname .SplitContainer1.Panel1.Controls.Add(lb) .SplitContainer1.Panel1.Controls.Add(tb) .AutoSize = True .Update() End With x = x + 1 End If Next End If tr.Commit() Next End Using Catch ex As Autodesk.AutoCAD.Runtime.Exception ed.WriteMessage(("Exception: " + ex.Message)) Finally End Try End Sub Public Sub OK_Click(sender As Object, e As EventArgs) Handles OK.Click Dim txtlist As New List(Of String)() Dim childc As System.Windows.Forms.Control For Each childc In Me.SplitContainer1.Panel1.Controls If TypeOf childc Is System.Windows.Forms.TextBox Then txtlist.Add(childc.Text.ToString) End If Next Dim txtarray As String() = txtlist.ToArray() Dim tagarray As String() = taglist.ToArray() Dim prmtarray As String() = promptlist.ToArray() Using (acDoc.LockDocument()) Using tr As Transaction = db.TransactionManager.StartTransaction() For z As Integer = 0 To txtarray.Length - 1 MsgBox(txtarray(z)) MsgBox(prmtarray(z)) MsgBox(tagarray(z)) MsgBox(br.Name) Dim btrIDs As ObjectIdCollection = btr.GetBlockReferenceIds(True, False) For Each id As ObjectId In btrIDs If Not id.IsEffectivelyErased Then 'Dim bref As BlockReference = id.GetObject(OpenMode.ForRead) Dim AttCol As AttributeCollection = br.AttributeCollection For Each AttID As ObjectId In AttCol Dim AttR As AttributeReference = tr.GetObject(AttID, OpenMode.ForRead, False) If AttR.Tag = tagarray(z) Then AttR.UpgradeOpen() AttR.TextString = txtarray(z) End If Next End If Next Next tr.Commit() End Using End Using Me.Close() Me.Dispose() Dispose() br.Dispose() btr.Dispose() bt.Dispose() End Sub Private Sub Cancel_Click(sender As Object, e As EventArgs) Handles Cancel.Click Me.Close() Me.Dispose() Dispose() End Sub End Class
Ron,
How is your code used. Should I cut and paste it in a form?
I am getting several errors cutting/pasting it in mycommands module.
Thank you,
You'll have to create the form for it. I used the designer, not code to do that. I use this as a separate windows form within my project.