AutoCAD C#: Modify Xdata value

AutoCAD C#: Modify Xdata value

mnpatric
Enthusiast Enthusiast
8,599 Views
16 Replies
Message 1 of 17

AutoCAD C#: Modify Xdata value

mnpatric
Enthusiast
Enthusiast

How can I modify the value of existing Xdata on the existing objects?  I was able to retrieve the Xdata values, but I have not been able to find a method to change the value.  So far I got the following code by Googling everywhere, but "aryXD[intXD].Value" seems to be read only, and I can't find the way to assign different value

 

TypedValue[] acTypValAr = new TypedValue[2]; //instanciate array 

 acTypValAr.SetValue(new TypedValue((int)DxfCode.ExtendedDataRegAppName,strAFM), 0); //archibus

 acTypValAr.SetValue(new TypedValue((int)DxfCode.ExtendedDataAsciiString, strCur), 1); //bldg code 

SelectionFilter acSelFtr = new SelectionFilter(acTypValAr); //assign 



//select

SelectionSet acSSet = acDocEd.SelectAll(acSelFtr).Value;

MessageBox.Show("object before: " + acSSet.Count.ToString());



foreach (SelectedObject acSSObj in acSSet)

{

if (acSSObj != null)

{

Entity acEnt = MyCommands.acTrans.GetObject(acSSObj.ObjectId, OpenMode.ForWrite) as Entity;

if (acEnt != null)

{

TypedValue[] aryXD = acEnt.XData.AsArray();

if (aryXD != null)

{

for (int intXD = 0; intXD < aryXD.Length; intXD++)

{

if (aryXD[intXD].TypeCode == 1000)

{

if (aryXD[intXD].Value.ToString() == strCur)

{
0 Likes
Accepted solutions (1)
8,600 Views
16 Replies
Replies (16)
Message 2 of 17

Anonymous
Not applicable

Hi Patric,

 

To my knowledge, XData record can’t be updated. But below workaround may help you achieve this.

  • Retrieve data from XData record
  • Delete/Dispose XData record from object
  • Create a new TypedValue/XData Record with updated values from retrieved data
  • Add this new record to object

 

Thanks,

Srikanth.

Message 3 of 17

mnpatric
Enthusiast
Enthusiast

thank you very much for your info.  that makes sense because when i tried to setvalue to the existing xdata, i get a fatal error.  i will try your method.

0 Likes
Message 4 of 17

mnpatric
Enthusiast
Enthusiast

if possible, could you please post a sample code?

0 Likes
Message 5 of 17

_gile
Consultant
Consultant
Accepted solution

Hi

 

Assuming strAFM is bound to your application name and strCur is bound to the string value you want to change:

 

Document doc = AcAp.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

TypedValue[] filter = { new TypedValue(1001, strAFM) };
PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
if (psr.Status != PromptStatus.OK) return;

using (Transaction tr = db.TransactionManager.StartTransaction())
{
    foreach (SelectedObject obj in psr.Value)
    {
        Entity ent = (Entity)tr.GetObject(obj.ObjectId, OpenMode.ForWrite);
        ResultBuffer resbuf = ent.GetXDataForApplication(strAFM);
        TypedValue[] data = resbuf.AsArray();
        for (int i = 0; i < data.Length; i++)
        {
            TypedValue tv = data[i];
            if (tv.TypeCode == 1000 && (string)tv.Value == strCur)
            {
                data[i] = new TypedValue(1000, "new value");
            }
        }
        resbuf = new ResultBuffer(data);
        ent.XData = resbuf;
    }
    tr.Commit();
}


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 17

mnpatric
Enthusiast
Enthusiast

thank you very much for your help

Message 7 of 17

Anonymous
Not applicable

hi,

 

greetings...

 

if we you AutoCAD COM API, How can I proceed. could you please support me

thanks

0 Likes
Message 8 of 17

_gile
Consultant
Consultant

Hi,

Have a look at the GetXData() and SetXData() methods.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 9 of 17

SoporteEspecializado
Contributor
Contributor

Hello My name is Danieel, I am trying to make sure that the xdata associated with an unexploded element can be maintained when the element is exploded.

 

{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);

DBObjectCollection objs = new DBObjectCollection();

foreach (ObjectId objId in btr)
{
ent = (Entity)tr.GetObject(objId, OpenMode.ForRead);
ent.Explode(objs);
ent.UpgradeOpen();
// ent.Erase();


}

foreach (DBObject obj in objs)
{
Entity ent1 = (Entity)obj;

TypedValue[] c = ent.XData.AsArray();
String nameref = c[0].Value.ToString();
idRef = c[3].Value.ToString();

btr.AppendEntity(ent1);
tr.AddNewlyCreatedDBObject(ent1, true);
ent1.XData = new ResultBuffer(new TypedValue(1001, nameref), new TypedValue(1070, 1), new TypedValue(1000, idRef));

}
tr.Commit();
db.SaveAs(rutanarchivo, DwgVersion.Current);
}

I only get the xdata of an element
Could someone help me?
 
0 Likes
Message 10 of 17

_gile
Consultant
Consultant

Hi,

 

Before exploding each entity within the current space, you should check if the entity is explodable and if it has (the required) xdata.

You should use a simple method which try to explode an entity maintaing the xdata (if any) in the resulting entities, so that you can try this method by selection a single entity and, when it suit your needs use this same method with all entities within the current space.

 

Here's an example of this kind of method:

        static DBObjectCollection TryExplodeMaintainingXdata(Entity entity)
        {
            try
            {
                var entitySet = new DBObjectCollection();
                var data = entity.XData;
                entity.Explode(entitySet);
                if (data != null)
                {
                    foreach (Entity ent in entitySet)
                    {
                        ent.XData = data;
                    }
                }
                return entitySet;
            }
            catch { return null; }
        }

And a testing command:

        [CommandMethod("XPLODETEST")]
        public static void ExplodeTest()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;
            var pr = ed.GetEntity("\nSelect entity to explode: ");
            if (pr.Status != PromptStatus.OK)
                return;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                var entity = (Entity)tr.GetObject(pr.ObjectId, OpenMode.ForWrite);
                var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                var entitySet = TryExplodeMaintainingXdata(entity);
                if (entitySet != null)
                {
                    foreach (Entity ent in entitySet)
                    {
                        curSpace.AppendEntity(ent);
                        tr.AddNewlyCreatedDBObject(ent, true);
                    }
                }
                else
                {
                    ed.WriteMessage("\nSelected entity cannot be exploded.");
                }
                tr.Commit();
            }
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 11 of 17

SoporteEspecializado
Contributor
Contributor

Thank you very much for everything, your comment helped me a lot and to be able to find a solution to what I needed
0 Likes
Message 12 of 17

SoporteEspecializado
Contributor
Contributor
Hello
Doing more tests with the code, it crashes when trying to manage large planes.
Attached code extract.

using (Database db = new Database(false, true))
{
try
{
db.ReadDwgFile(filedwg, FileOpenMode.OpenForReadAndAllShare, false, "");
}

catch (System.Exception)
{
ed.WriteMessage("\nUnable to read drawing file.");
return;
}
PromptResult sigla = ed.GetString("Ingrese las propiedades para las capas");
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
foreach (ObjectId objId in btr)
{
var entity = (Entity)tr.GetObject(objId, OpenMode.ForWrite);
var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
var entitySet = TryExplodeMaintainingXdata(entity);

if (entitySet != null)
{
foreach (Entity ent in entitySet)
{
curSpace.AppendEntity(ent);
tr.AddNewlyCreatedDBObject(ent, true);
}
}
else
{
ed.WriteMessage("\nSelected entity cannot be exploded.");
}

}
tr.Commit();
db.SaveAs(rutanarchivo, DwgVersion.Current);
}
}

 

 

 

static DBObjectCollection TryExplodeMaintainingXdata(Entity entity)
{
try
{
var entitySet = new DBObjectCollection();
var data = entity.XData;
entity.Explode(entitySet);
if (data != null)
{
foreach (Entity ent in entitySet)
{
ent.XData = data;
}
}
return entitySet;
}
catch { return null; }
}

 

 

0 Likes
Message 13 of 17

_gile
Consultant
Consultant

It looks like you copy/paste code snippets you do not fully understand.

You're opening the currents pace for write twice.

(BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 14 of 17

SoporteEspecializado
Contributor
Contributor
Excuse me, I'm new to the world of programming and autocad, I try to learn as they ask me.
0 Likes
Message 15 of 17

_gile
Consultant
Consultant

No need to apologize.
Assuming that you are new to AutoCAD programming, you should start with simple tasks so that you can write the code by yourself.
For example, in this case, don't try to do batch processing right away by using in-memory databases (with ReadDwgFile) to explode all entities in the current space of each.
Start by learning how to explode a single entity as I showed you.
Then try to write a method that exploses all the entities in the current space so you can test it in the current drawing. And only when you are satisfied that the method works, you can use it to do batch processing.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 16 of 17

SoporteEspecializado
Contributor
Contributor
I followed your advice. I made some modifications and it worked.
Change the "ed.GetEntity"
by "ed.SelectAll()" which allowed me to make a selection to all the elements and add a filter so that it would only take the block reference that I needed to change Thanks for your help


[CommandMethod("XPLODETEST")]
public static void ExplodeTest()
{
var doc = Application.DocumentManager.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;

PromptSelectionResult pr = ed.SelectAll();

if (pr.Status == PromptStatus.OK)
{
using (var tr = db.TransactionManager.StartTransaction())
{
foreach (var id in pr.Value.GetObjectIds())
{
var entity = (Entity)tr.GetObject(id, OpenMode.ForWrite);
if (entity.GetType().Name == "BlockReference")
{
var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
var entitySet = TryExplodeMaintainingXdata(entity);
if (entitySet != null)
{
foreach (Entity ent in entitySet)
{
curSpace.AppendEntity(ent);
tr.AddNewlyCreatedDBObject(ent, true);
entity.Erase();
}
}
else
{
ed.WriteMessage("\nSelected entity cannot be exploded.");
}
}
}
tr.Commit();

}
}
}
0 Likes
Message 17 of 17

_gile
Consultant
Consultant

Happy to know you get it work.

 

Just some improvements to your code.

If you use SelectAll, take care it select entities in all the layouts (model and paer spaces). A good practice with a selection is to use a SelectionFilter. In this case you should filter on the entity type (INSERT for block references and the current space).

Do not make things in loops (foreach, for, while) if they could be done only once outside of the loop scope.

 

Here's an example:

        [CommandMethod("XPLODETEST1")]
        public static void ExplodeTest1()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            // Create a filter to select all the block references in the current space
            string ctab = (string)Application.GetSystemVariable("CTAB");
            var filter = new SelectionFilter(new[] {
                new TypedValue(0, "INSERT"),
                new TypedValue(410, ctab) });
            PromptSelectionResult pr = ed.SelectAll(filter);

            if (pr.Status == PromptStatus.OK)
            {
                using (var tr = db.TransactionManager.StartTransaction())
                {
                    // Open the current space outside of the foreach loop
                    var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                    foreach (var id in pr.Value.GetObjectIds())
                    {
                        // We know all entities within the selection set are block references
                        var blockRef = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
                        var entitySet = TryExplodeMaintainingXdata(blockRef);
                        if (entitySet != null)
                        {
                            foreach (Entity ent in entitySet)
                            {
                                curSpace.AppendEntity(ent);
                                tr.AddNewlyCreatedDBObject(ent, true);
                            }
                            // Erase the source block reference outside of the foearch loop
                            blockRef.Erase();
                        }
                        else
                        {
                            ed.WriteMessage("\nSelected entity cannot be exploded.");
                        }
                    }
                    tr.Commit();
                }
            }
        }

 

Instead of SelectAll, you could also directly iterate throug all the entities int the current space checking for the entity type to get only block references. This method is not slower than the SelectAll with filter which internally does the same thing.

        [CommandMethod("XPLODETEST2")]
        public static void ExplodeTest2()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            using (var tr = db.TransactionManager.StartTransaction())
            {
                // Open the current space
                var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                foreach (ObjectId id in curSpace)
                {
                    // Check if the id is one of a block reference
                    if (id.ObjectClass.DxfName == "INSERT")
                    {
                        var blockRef = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
                        var entitySet = TryExplodeMaintainingXdata(blockRef);
                        if (entitySet != null)
                        {
                            foreach (Entity ent in entitySet)
                            {
                                curSpace.AppendEntity(ent);
                                tr.AddNewlyCreatedDBObject(ent, true);
                            }
                            // Erase the source block reference outside of the foreach loop
                            blockRef.Erase();
                        }
                        else
                        {
                            ed.WriteMessage("\nSelected entity cannot be exploded.");
                        }
                    }
                }
                tr.Commit();
            }
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub