The code you wrote works OK when every attribute tag in a block is different. This is the recommended practice and in recent versions of AutoCAD they actively discourage you from duplicating tags by coloring them red in EATTEDIT. Your code breaks down, however, when a block has multiple attributes using the same tag. One of the older title blocks at this company was set up that way, so I wrote a version that handles this better, by using the position of the attribute in the order of the list of attributes in the block (after matching by tag). This better matches how ATTEDIT and EATTEDIT appear to work.
Public Shared Function GetAttributePrompt(ByRef Att As AttributeReference, ByVal DB As Database, ByVal Trans As Transaction) As String
ValidateDBandTrans(DB, Trans, OpenMode.ForRead)
If IsNothing(Att) Then Throw New ArgumentNullException("Att")
'find out index position of attrib in block reference
Dim BlkRef As BlockReference = DirectCast(Trans.GetObject(Att.OwnerId, OpenMode.ForRead), BlockReference)
Dim attindex As Integer = 0
For Each objID As ObjectId In BlkRef.AttributeCollection
Dim AttRef As AttributeReference = DirectCast(objID.GetObject(OpenMode.ForRead), AttributeReference)
If AttRef.Tag = Att.Tag Then
If objID = Att.ObjectId Then Exit For
attindex += 1
End If
Next
'attindex should now be which attribute of this tag (#0, #1, etc)
Dim Blocks As BlockTable = DirectCast(Trans.GetObject(DB.BlockTableId, OpenMode.ForRead), BlockTable)
Dim BlockDef As BlockTableRecord = DirectCast(Trans.GetObject(Blocks.Item(BlkRef.Name), OpenMode.ForRead), BlockTableRecord)
Dim ctr As Integer = 0
For Each objID As ObjectId In BlockDef
If objID.ObjectClass.Name = "AcDbAttributeDefinition" Then
Dim AttDef As AttributeDefinition = DirectCast(objID.GetObject(OpenMode.ForRead), AttributeDefinition)
If AttDef.Tag = Att.Tag Then
'maybe it's the right one, maybe not, check the count
If ctr = attindex Then
Return AttDef.Prompt
End If
ctr += 1
End If
End If
Next
Return ""
'Throw New ApplicationException("Cannot find prompt for this attribute reference.")
End Function
Private Shared Sub ValidateDBandTrans(ByVal DB As Database, ByVal Trans As Transaction)
If IsNothing(DB) Then Throw New ArgumentNullException("DB")
If IsNothing(Trans) Then Throw New ArgumentNullException("Trans")
If DB.IsDisposed Then Throw New ArgumentException("Database has been disposed.")
If Trans.IsDisposed Then Throw New ArgumentException("Transaction has been disposed.")
End Sub
I tested this on our title block and it seems to work right. An interesting situation comes up if the user uses "Edit Block In-Place" and deletes an attribute. The prompt strings shown in the ATTEDIT or EATTEDIT shift position. For example:
Originally Tag, Prompt, and Text
DOOR_NOTE Door #1 1
DOOR_NOTE Door #2 2
DOOR_NOTE Door #3 3
Delete Attribute 2 using "Edit Block In-Place", attribute still exists in BlockReference, ATTSYNC was not run
DOOR_NOTE Door #1 1
DOOR_NOTE Door #3 2
DOOR_NOTE 3
ATTEDIT shows the tag on screen in place of the Prompt, EATTEDIT shows a blank. I made my code return a blank and to shift the prompts up the list like the AutoCAD commands do, but it goes to show that one should be careful about making assumptions that you have the correct PromptString. This returns what is right in AutoCAD's eyes, but is conceptually the wrong one for the user.
For anyone that is interested, I tested this using:
<CommandMethod("GetAttrib")> _
Public Sub TestGetAttributePrompt()
Try
Dim DB As Database = HostApplicationServices.WorkingDatabase
Dim ed As Autodesk.AutoCAD.EditorInput.Editor = Application.DocumentManager.GetDocument(DB).Editor
Using Trans As Transaction = DB.TransactionManager.StartTransaction()
Dim blkrefs As List(Of BlockReference) = AcadTransactionFunctions.GetBlockReferencesLike("AWL Title", True, DB, Trans, OpenMode.ForRead)
If blkrefs.Count > 0 Then
Dim attribs As AttributeCollection = blkrefs(0).AttributeCollection
Dim prompt As String
For Each objID As ObjectId In attribs
Dim attrib As AttributeReference = DirectCast(objID.GetObject(OpenMode.ForRead), AttributeReference)
ed.WriteMessage("Tag: " & attrib.Tag & vbLf)
prompt = AcadTransactionFunctions.GetAttributePrompt(attrib, DB, Trans)
ed.WriteMessage("Prompt: " & prompt & vbLf)
ed.WriteMessage("Text: " & attrib.TextString & vbLf)
Next
End If
Trans.Commit()
End Using
Catch ex As System.Exception
Application.ShowAlertDialog("Error: " & ex.Message)
End Try
End Sub