.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Class to handle XDictionary/Xrecords

7 REPLIES 7
SOLVED
Reply
Message 1 of 8
ilovejingle
2888 Views, 7 Replies

Class to handle XDictionary/Xrecords

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!

7 REPLIES 7
Message 2 of 8
norman.yuan
in reply to: ilovejingle

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

Drive CAD With Code

EESignature

Message 3 of 8
ilovejingle
in reply to: norman.yuan

Thanks for the code, really appreciate it.
But actually I intend.to use Xdictionary +Xrecord because I have more data to attach in future. Xdata will not serve the purpose.
I understand your code in Xdata scenario, but I am still confused why you say the sync method didnt.serve the purpose, it updates the value in the xrecord in dbdictionary which is attached to entity, right?
How the code can be modified if I want.to acheieve exactly the same thing?
Message 4 of 8
ilovejingle
in reply to: norman.yuan

I mean as you said in the last paragraph, how the code is different.or how the logic should be different if I want.to achieve the same thing using extension dictionary
Message 5 of 8
_gile
in reply to: ilovejingle

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();
                }
            }
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 8
_gile
in reply to: _gile

Oopss!..

With these extension methods:

var ent = (Entity)tr.GetObject(per.ObjectId, OpenMode.ForRead);

should have been simply written:

var ent = per.ObjectId.GetObject<Entity>();


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 7 of 8
ilovejingle
in reply to: norman.yuan

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
Message 8 of 8
ilovejingle
in reply to: _gile

Thanks! I learnt something new.

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Technology Administrators