CAD data is not limited to layers, linetypes, and point coordinates. Let's make an application that reads and manipulates data that you store in AutoCAD objects. While our example is simple, in the real world this data could represent material type, weight, cost, inventory, supplier data, and more. We saw how we can attach attributes (text data) to block references in AutoCAD drawings. However, we work with other AutoCAD object types in addition to blocks, and we may also wish to represent data using other data types (integers, reals, 2D and 3D points, and more). We can accomplish this by using extended entity data, or xdata for short.
In this project, which can be used with virtually any AutoCAD drawing, we will create commands which add (attach) xdata to an AutoCAD object, read and display xdata found on an object, and also remove specific xdata from an object.
Create a new command,
AddXD
. Get the current drawing'sDatabase
andEditor
objects:[CommandMethod("AddXD")] static public void AddXD() { //Get the curr dwg database Database db = Application.DocumentManager.MdiActiveDocument.Database; //Begin a transaction using (Transaction trans = db.TransactionManager.StartTransaction()) { //Get the Editor object of current drawing Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Add code to prompt the user to select an object on screen (configure the options to exit the command if the user right-clicks on or presses ENTER):
//Select an object on screen PromptEntityOptions prEntOpts = new PromptEntityOptions( "Select object"); prEntOpts.AllowNone = true; //Allow Enter key/Right-click as input PromptEntityResult prEntRes = ed.GetEntity(prEntOpts); if (prEntRes.Status != PromptStatus.OK) { ed.WriteMessage("Done."); return; }
Open the selected object for read. Retrieve the handle of the object, as well as the object's layer name. Now, upgrade the
OpenMode
toForWrite
, since we will be writing xdata to the object://Open the selected object for read Entity acEnt = (Entity)trans.GetObject(prEntRes.ObjectId, OpenMode.ForRead); //Get the handle for the selected object Handle acHndl = acEnt.Handle; //Get the layer name for the selected object string lyrName = acEnt.Layer.ToString(); acEnt.UpgradeOpen();
Open the
regapp
table and check for your appid (a unique identifier for accessing this set of data). I suggest changing it fromTEST
to something more original. If your appid is not found, upgrade theregapp
table'sOpenMode
toForWrite
, create a newregapp
table record and then add your appid:RegAppTable regTbl = (RegAppTable)trans.GetObject(db.RegAppTableId, OpenMode.ForRead); if (!regTbl.Has("TEST")) { regTbl.UpgradeOpen(); RegAppTableRecord regTblRec = new RegAppTableRecord(); regTblRec.Name = "TEST"; regTbl.Add(regTblRec); trans.AddNewlyCreatedDBObject(regTblRec, true); }
Finally, attach a variety of xdata types to the selected AutoCAD object. Add the xdata to a
ResultBuffer
and set the object's xdata property to the contents of the buffer. Once the xdata is attached, dispose of theResultBuffer
:ResultBuffer resBuf = new ResultBuffer(new TypedValue(1001, "TEST"), new TypedValue(1000, "An integer:"), new TypedValue(1070, 0521), new TypedValue(1000, "Handle of selected object:"), //Handle of the object you picked new TypedValue(1005, acHndl), new TypedValue(1000, "Open bracket:"), //'Opening' control bracket for nested xdata new TypedValue(1002, "{"), new TypedValue(1000, "Layer of selected object:"), new TypedValue(1003, acEnt.Layer.ToString()), new TypedValue(1000, "Close bracket"), //'Closing' control bracket for nested xdata new TypedValue(1002, "}")); acEnt.XData = resBuf; resBuf.Dispose(); trans.Commit(); } //end transaction
Create another command,
ReadXD
. As before, get theDatabase
andEditor
objects, and prompt the user to select an object. Also, check theregapp
table for your appid:[CommandMethod("ReadXD")] static public void ReadXD() { //Get the curr dwg database Database db = Application.DocumentManager.MdiActiveDocument.Database; //Begin a transaction using (Transaction trans = db.TransactionManager.StartTransaction()) { //Get the Editor object of current drawing Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; //Select an object on screen PromptEntityOptions prEntOpts = new PromptEntityOptions("Select object"); prEntOpts.AllowNone = true; //Allow Enter key/Right-click PromptEntityResult prEntRes = ed.GetEntity(prEntOpts); if (prEntRes.Status != PromptStatus.OK) { ed.WriteMessage("Done."); return; } //Open the selected object for read Entity acEnt = (Entity)trans.GetObject(prEntRes.ObjectId, OpenMode.ForRead); //Open the regapp table and check for your appid RegAppTable regTable = (RegAppTable)trans.GetObject(db.RegAppTableId, OpenMode.ForRead);
If your appid exists in the table, retrieve the xdata from the selected object. Use a
ResultBuffer
to capture the xdata. Loop through the buffer, testing each item for its xdata type code. Display the xdata (adding some descriptive information) in AutoCAD's textscreen (press the F2 key to toggle the textscreen visibility):if (regTable.Has("TEST")) { //Replace "TEST" with your own appid //If the appid exists in the regapp table, attempt to read the //object's xdata from the table ResultBuffer resBuf = acEnt.GetXDataForApplication("TEST"); if (resBuf != null) { TypedValue[] resBufArray = resBuf.AsArray(); foreach (TypedValue val in resBufArray) { switch ((DxfCode)val.TypeCode) { //Appid type xdata case DxfCode.ExtendedDataRegAppName: string appid = (string)val.Value; ed.WriteMessage(System.Environment.NewLine + "1001 AppId: {0}", appid); break; //String type xdata case DxfCode.ExtendedDataAsciiString: string strng = (string)val.Value; ed.WriteMessage(System.Environment.NewLine + "1000 String: {0}", strng); break; //LayerName type xdata case DxfCode.ExtendedDataLayerName: string laynam = (string)val.Value; ed.WriteMessage(System.Environment.NewLine + "1003 Layer: {0}", laynam); break; //Handle type xdata case DxfCode.ExtendedDataHandle: ed.WriteMessage(System.Environment.NewLine + "1005 Handle: {0}", val.Value); break; //Control type xdata case DxfCode.ExtendedDataControlString: string brkt = (string)val.Value; ed.WriteMessage(System.Environment.NewLine + "1002 Control: {0}", brkt); break; //Integer type xdata case DxfCode.ExtendedDataInteger16: Int16 intgr = (short)val.Value; ed.WriteMessage(System.Environment.NewLine + "1070 Integer: {0}", intgr); break; default: ed.WriteMessage(System.Environment.NewLine + "Unknown type: {0}", val.Value); break; }//switch }//foreach } else { ed.WriteMessage(System.Environment.NewLine + "No xdata found for appid: TEST"); } trans.Commit(); } else { ed.WriteMessage(System.Environment.NewLine + "No xdata found for appid: TEST"); }
Create a command named
RemXD
to remove xdata associated with a particular appid. Add code, as in the other two commands, to prompt the user to select an object:[CommandMethod("RemXD")] static public void RemXD() { //Get the curr dwg database Database db = Application.DocumentManager.MdiActiveDocument.Database; //Begin a transaction using (Transaction trans = db.TransactionManager.StartTransaction()) { Editor ed = Application.DocumentManager.MdiActiveDocument.Editor; //Select an object on screen PromptEntityOptions prEntOpts = new PromptEntityOptions("Select object"); prEntOpts.AllowNone = true; //Allow Enter key/Right-click PromptEntityResult prEntRes = ed.GetEntity(prEntOpts); if (prEntRes.Status != PromptStatus.OK) { ed.WriteMessage("Done."); return; } //Open the selected object for read Entity acEnt = (Entity)trans.GetObject(prEntRes.ObjectId, OpenMode.ForRead); ResultBuffer resBuf = acEnt.GetXDataForApplication("TEST");
If there is xdata associated with your appid, remove it by setting the object's
XData
property to an emptyResultBuffer
(empty, except for your appid):if (resBuf != null) { acEnt.UpgradeOpen(); acEnt.XData = new ResultBuffer(new TypedValue(1001,"TEST")); resBuf.Dispose(); } trans.Commit(); }
AutoCAD maintains a regapp
table which contains a regapp
table record for every appid. Xdata persists with an object (entity) between drawing sessions. You can have more than one appid associated with an object, but you can only retrieve the xdata associated with a particular appid. There is also an xdata command for end users to specify an appid and add XData
to an object. The main disadvantage of xdata is its size limitation. It is recommended to have no more than 1 KB of xdata attached to any one AutoCAD object.
If you require more data per object, AutoCAD has an alternative to xdata called the xrecord, which lives either in the drawing's NamedObject Dictionary (NOD) or in a specific object's extension dictionary. Unlike xdata, xrecords are not subject to the 1 KB size limitation and hold much more data than xdata. Working with xrecords is outside the scope of this book.