Inspired by the answers below
https://forums.autodesk.com/t5/net/how-to-indexing-a-resultbuffer-in-vb-net-or-c/td-p/8184679\
I am writing a class to handle the xdictionary and xrecords for entity in AutoCAD.
Basically I have two properties a string "Type" and a double "Length"
The constructor of the class will retrieve the resultbuffer using GetXDataForApplication(appName) and check whether the resultbuffer is empty. if it is, it will change the class variable "exist" to false, and otherway around
the class will have properties called "type" and "Length", which both have set and get method.
The method called "Sync" will grab all values in the class and fill them into the extension dictionary for the Entity.
I am also writing some code to test whether the class works properly. the problem is each time I run the code to the same entity. the constructor is not able to grab the resultbuffer and change the class object's exist variable. so each time it shows the result buffer is empty. no idea why..
Really appreciate if anybody can help me debug the code a bit. or other better ways it can be achieved.
class code :
Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.EditorInput Public Class BESDXData Private entID As ObjectId Private convType As String Private convLength As Double Private appName As String = "BESD" Public Exist As Boolean Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Public Sub New(objEnt As DBObject) entID = objEnt.ObjectId Dim rb As ResultBuffer = objEnt.GetXDataForApplication(appName) Dim ed As Editor = doc.Editor If IsNothing(rb) Then ed.WriteMessage(vbLf + "Result Buffer is empty !") Exist = False Else ed.WriteMessage(vbLf + "Result Buffer is not empty !") Exist = True End If End Sub Property Type As String Get Return convType End Get Set(value As String) convType = value End Set End Property Property Length As Double Get Return convLength End Get Set(value As Double) convLength = value End Set End Property Public Sub Sync() Using trans As Transaction = doc.TransactionManager.StartTransaction 'Get the DB object from the transaction Dim objEnt As DBObject = trans.GetObject(entID, OpenMode.ForRead) 'Check whether it contains the extension dictionary. if not create the dictionary. Dim dictID As ObjectId = objEnt.ExtensionDictionary If dictID = ObjectId.Null Then objEnt.UpgradeOpen() objEnt.CreateExtensionDictionary() dictID = objEnt.ExtensionDictionary End If 'Now the dictionray is already created, then we check whether old Xrecord is in it 'First get the dictionary object Dim objDict As DBDictionary = trans.GetObject(dictID, OpenMode.ForRead) 'Build the new Result buffer to hold the new values. Dim rb As New ResultBuffer From { New TypedValue(DxfCode.ExtendedDataAsciiString, Type), New TypedValue(DxfCode.ExtendedDataReal, Length) } 'If the dictionary constains the app and objectid of old xrecord 'we should modify the old xrecord from database. If objDict.Contains(appName) Then objDict.UpgradeOpen() Dim xRec As Xrecord = trans.GetObject(objDict.GetAt(appName), OpenMode.ForWrite) xRec.Data = rb Else Dim xRec As New Xrecord objDict.UpgradeOpen() xRec.Data = rb objDict.SetAt(appName, xRec) trans.AddNewlyCreatedDBObject(xRec, True) End If trans.Commit() End Using End Sub End Class
Test code of mine, I try to run it on multiple times on the same entity in AutoCAD.
<CommandMethod("TestBESD")> Sub testBESD() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim ed As Editor = doc.Editor Dim db As Database = doc.Database Dim optEnt As New PromptEntityOptions(vbLf + "Please Select an Entity to Add BESD Data : ") With { .AllowNone = False, .AllowObjectOnLockedLayer = False } Dim resEnt As PromptEntityResult = ed.GetEntity(optEnt) If Not resEnt.Status = PromptStatus.OK Then Exit Sub End If Using trans As Transaction = db.TransactionManager.StartTransaction Dim objEnt As DBObject = trans.GetObject(resEnt.ObjectId, OpenMode.ForRead) Dim besdData As New BESDXData(objEnt) If besdData.Exist = True Then ed.WriteMessage(vbLf + "X_Records exist") ed.WriteMessage(vbLf + "Length is " + besdData.Length.ToString) ed.WriteMessage(vbLf + "Type is " + besdData.Type) Else besdData.Length = 1000 besdData.Type = "T11" besdData.Sync() ed.WriteMessage(vbLf + "X_Records added" + vbLf + "Type : " + besdData.Type + vbLf + "Length : " + besdData.Length.ToString) End If trans.Commit() End Using End Sub End Class
Thank you very much!
Solved! Go to Solution.
Solved by _gile. Go to Solution.
Solved by norman.yuan. Go to Solution.
It seems that you are confusing XData (Extensed Data) with ExtensionDictionary, which I also noticed in your previous post.
XData is a set of ResultBuffers grouped by RegistredApplication, being attached to Entities, while ExtensionDictionary itself is a DBObject(DBDictionary) that usually contains either DBDictionary, or XRecord, indexed by named keys.
For XData's ResultBuffer (TypedValue array), its data type can only be DxfCode.ExtendedDataXXXXXX.
So, while you can attatch both XData and ExtensionDictionary to an Entity, you need to know they are different things and treat they seperately. Thus, the Sync() method in your code doe not make much sense for what you want to do, if you are to use XData.
Since your requirement is fairly easy (simply attach 2 pieces of data, a string and a double) to an entity, it would fairly easy. I just make some changes with your code (but I prefer to make the process as and extension method to an Entity, or better yet, to ObjectId):
Public Class BESDXData Private entID As ObjectId Private convType As String Private convLength As Double Private appName As String = "BESD" Public Exist As Boolean ''Dim doc As Document = Application.DocumentManager.MdiActiveDocument ''Dim db As Database = doc.Database Public Sub New(objEntId As ObjectId) entID = objEntId
Using tran As Transaction = entId.Database.TransactionManager.StartTransaction()
Dim objEnt = tran.GetObject(entId, OpenMode.ForRead) Dim rb As ResultBuffer = objEnt.GetXDataForApplication(appName) ''Dim ed As Editor = doc.Editor If IsNothing(rb) Then ''ed.WriteMessage(vbLf + "Result Buffer is empty !") Exist = False Else '' Initialise member field with retrieved XData here
Dim vals = rb.AsArray()
If vals.Length = 3 Then
'' First element is the application name
convType=vals(1).Value.ToString()
convLength=Convert.ToDouble(vals(2).Value) Exist = True
Else
Exist = False
End If End If
tran.Commit()
End Using End Sub Property Type As String Get Return convType End Get Set(value As String) convType = value End Set End Property Property Length As Double Get Return convLength End Get Set(value As Double) convLength = value End Set End Property Public Sub Sync() Using trans As Transaction = entId.Database.TransactionManager.StartTransaction
Dim objEnt As DBObject = tran.GetObject(entId, OpenMode.ForWrite)
Dim vals As TypedValue(0 to 2)
vals(0)=New TypedValue(DxfCode.ExtendedDataRegAppName, appName)
vals(1)=New TypedValue(DfCode.ExtendedDataAsciiString, convType)
vals(2)=New TypedValue(DxfCode.ExtendedDataReal, convLength)
Dim rb As ResultBuffer = New ResultBuffer(vals)
objEnt.XData = rb trans.Commit() End Using End Sub End Class
If you want to store data with entity as ExtensionDictionary, the code would be different. Again, XData and ExtensionDictionary are different things.
Norman Yuan
Hi,
For my part, I prefer to use extension methods.
With VB, to add the class (module with VB) to the Autodesk.AutoCAD.DatabaseServices namespace you should have to clear the 'root namespace'.
using System; using Autodesk.AutoCAD.Runtime; namespace Autodesk.AutoCAD.DatabaseServices { public static class Extension { public static T GetObject<T>( this ObjectId id, OpenMode mode = OpenMode.ForRead, bool openErased = false, bool forceOpenOnLockedLayer = false) where T : DBObject { if (id.IsNull) throw new Runtime.Exception(ErrorStatus.NullObjectId); Transaction tr = id.Database.TransactionManager.TopTransaction; if (tr == null) throw new Runtime.Exception(ErrorStatus.NoActiveTransactions); return (T)tr.GetObject(id, mode, openErased, forceOpenOnLockedLayer); } public static DBDictionary TryGetExtensionDictionary(this DBObject source) { if (source == null) throw new ArgumentNullException(nameof(source)); ObjectId dictId = source.ExtensionDictionary; if (dictId == ObjectId.Null) { return null; } return dictId.GetObject<DBDictionary>(); } public static DBDictionary GetOrCreateExtensionDictionary(this DBObject source) { if (source == null) throw new ArgumentNullException(nameof(source)); if (source.ExtensionDictionary == ObjectId.Null) { source.UpgradeOpen(); source.CreateExtensionDictionary(); } return source.ExtensionDictionary.GetObject<DBDictionary>(); } public static ResultBuffer GetXDictionaryXrecordData(this DBObject source, string key) { if (source == null) throw new ArgumentNullException(nameof(source)); if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); DBDictionary xdict = source.TryGetExtensionDictionary(); if (xdict == null) { return null; } return xdict.GetXrecordData(key); } public static void SetXDictionaryXrecordData(this DBObject target, string key, params TypedValue[] values) { target.SetXDictionaryXrecordData(key, new ResultBuffer(values)); } public static void SetXDictionaryXrecordData(this DBObject target, string key, ResultBuffer data) { if (target == null) throw new ArgumentNullException(nameof(target)); if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); target.GetOrCreateExtensionDictionary().SetXrecordData(key, data); } public static ResultBuffer GetXrecordData(this DBDictionary dict, string key) { if (dict == null) throw new ArgumentNullException(nameof(dict)); if (!dict.Contains(key)) { return null; } ObjectId id = (ObjectId)dict[key]; if (id.ObjectClass != RXObject.GetClass(typeof(Xrecord))) return null; return id.GetObject<Xrecord>().Data; } public static void SetXrecordData(this DBDictionary dict, string key, ResultBuffer data) { if (dict == null) throw new ArgumentNullException(nameof(dict)); if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); Transaction tr = dict.Database.TransactionManager.TopTransaction; if (tr == null) throw new Runtime.Exception(ErrorStatus.NoActiveTransactions); Xrecord xrec; if (dict.Contains(key)) { xrec = ((ObjectId)dict[key]).GetObject<Xrecord>(OpenMode.ForWrite); } else { dict.UpgradeOpen(); xrec = new Xrecord(); dict.SetAt(key, xrec); tr.AddNewlyCreatedDBObject(xrec, true); } xrec.Data = data; } } }
Testing example:
[CommandMethod("XRECTEST")] public static void XrecordTest() { var doc = Application.DocumentManager.MdiActiveDocument; var db = doc.Database; var ed = doc.Editor; var per = ed.GetEntity("\nSelect object: "); if (per.Status == PromptStatus.OK) { using (var tr = db.TransactionManager.StartTransaction()) { var ent = (Entity)tr.GetObject(per.ObjectId, OpenMode.ForRead); var data = ent.GetXDictionaryXrecordData("TestXrec"); if (data == null) { ed.WriteMessage("\nThe entity does not have a 'TextXrec' Xrecord, it will be added"); ent.SetXDictionaryXrecordData("TestXrec", new TypedValue(1, "foo"), new TypedValue(70, 42)); } else { foreach (var tv in data.AsArray()) { ed.WriteMessage($"\nTypeCode: {tv.TypeCode}, Value: {tv.Value}"); } } tr.Commit(); } } }
I have rewrite the class and test code, now it seems to work.
class code:
Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.EditorInput Public Class BESDXData Private entID As ObjectId Private convType As String Private convLength As Double Private appName As String = "BESD" Public Exist As Boolean = False Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim db As Database = doc.Database Public Sub New(objEnt As DBObject) entID = objEnt.ObjectId Dim ed As Editor = doc.Editor Dim rb As ResultBuffer 'Dim rb As ResultBuffer = objEnt.GetXDataForApplication(appName) Dim dictID As ObjectId = objEnt.ExtensionDictionary If dictID = ObjectId.Null Then ed.WriteMessage(vbLf + "No dictionary ! ") Else Using trans As Transaction = db.TransactionManager.StartTransaction Dim objDict As DBDictionary = trans.GetObject(dictID, OpenMode.ForRead) Dim xRecID As ObjectId = objDict(appName) If xRecID = ObjectId.Null Then ed.WriteMessage(vbLf + "No Xrecord ! ") Else Dim xRec As Xrecord = trans.GetObject(xRecID, OpenMode.ForRead) rb = xRec.Data If IsNothing(rb) Then ed.WriteMessage(vbLf + "Result Buffer is empty ! " + rb.ToString) Else ed.WriteMessage(vbLf + "Result Buffer is not empty !") Exist = True Dim rbData() As TypedValue = rb.AsArray Type = rbData(0).Value Length = rbData(1).Value End If End If trans.Commit() End Using End If End Sub Property Type As String Get Return convType End Get Set(value As String) convType = value End Set End Property Property Length As Double Get Return convLength End Get Set(value As Double) convLength = value End Set End Property Public Sub Sync() Using trans As Transaction = doc.TransactionManager.StartTransaction 'Get the DB object from the transaction Dim objEnt As DBObject = trans.GetObject(entID, OpenMode.ForRead) 'Check whether it contains the extension dictionary. if not create the dictionary. Dim dictID As ObjectId = objEnt.ExtensionDictionary If dictID = ObjectId.Null Then objEnt.UpgradeOpen() objEnt.CreateExtensionDictionary() dictID = objEnt.ExtensionDictionary End If 'Now the dictionray is already created, then we check whether old Xrecord is in it 'First get the dictionary object Dim objDict As DBDictionary = trans.GetObject(dictID, OpenMode.ForRead) 'Build the new Result buffer to hold the new values. Dim rb As New ResultBuffer From { New TypedValue(DxfCode.ExtendedDataAsciiString, Type), New TypedValue(DxfCode.ExtendedDataReal, Length) } 'If the dictionary constains the app and objectid of old xrecord 'we should modify the old xrecord from database. If objDict.Contains(appName) Then objDict.UpgradeOpen() Dim xRec As Xrecord = trans.GetObject(objDict.GetAt(appName), OpenMode.ForWrite) xRec.Data = rb Else Dim xRec As New Xrecord objDict.UpgradeOpen() xRec.Data = rb objDict.SetAt(appName, xRec) trans.AddNewlyCreatedDBObject(xRec, True) End If trans.Commit() End Using End Sub End Class
test Code
<CommandMethod("TestBESD")> Sub testBESD() Dim doc As Document = Application.DocumentManager.MdiActiveDocument Dim ed As Editor = doc.Editor Dim db As Database = doc.Database Dim optEnt As New PromptEntityOptions(vbLf + "Please Select an Entity to Add BESD Data : ") With { .AllowNone = False, .AllowObjectOnLockedLayer = False } Dim resEnt As PromptEntityResult = ed.GetEntity(optEnt) If Not resEnt.Status = PromptStatus.OK Then Exit Sub End If Using trans As Transaction = db.TransactionManager.StartTransaction Dim objEnt As DBObject = trans.GetObject(resEnt.ObjectId, OpenMode.ForRead) Dim besdData As New BESDXData(objEnt) If besdData.Exist = True Then ed.WriteMessage(vbLf + "X_Records exist") ed.WriteMessage(vbLf + "Length is " + besdData.Length.ToString) ed.WriteMessage(vbLf + "Type is " + besdData.Type) Else besdData.Length = 1000 besdData.Type = "T11" besdData.Sync() ed.WriteMessage(vbLf + "X_Records added" + vbLf + "Type : " + besdData.Type + vbLf + "Length : " + besdData.Length.ToString) End If trans.Commit() End Using End Sub
Can't find what you're looking for? Ask the community or share your knowledge.