This entry completes the series of posts about per-document data. Here are the previous entries:
Some background to AutoCAD's MDI implementation and per-document data
Per-document data in ObjectARX
Per-document data in AutoCAD .NET applications - Part 1
Document.UserData
Now let's take a look at a second technique in .NET for storing transient (non-persisted) data with an AutoCAD document, the UserData property. The managed framework for AutoCAD associates a hash table with each document, which can be accessed using the UserData property. Hash tables are a great way to store and access data quickly: each object you store in a hash table is associated with a particular key, or lookup value. You then use this key to get at the data you've stored in the hash table.
The UserData property returns an object of the standard .NET class, System.Collections.Hashtable, so it’s advised to look on MSDN for further examples of usage.
I've written some code to demonstrate how you might store and access per-document data in the UserData property. The below C# code declares and implements a simple class called MyData to store custom data, and then two commands that store data in and use data from the UserData hash table.
- The “inc” command checks whether there’s an object under a particular key, and if not, it creates and adds a MyData object. It then goes on to increment the integer value stored in that object (and therefore in the hash table)
- The “bogus” command has some fun with the hash table, storing a bogus object (a Point2D object) in the place where the "inc" command expects to find a MyData object. This can only be run on a fresh drawing – one that has not had “inc” executed – otherwise there will already be an object stored in the hash table
Here's the code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using System.Collections;
[assembly: CommandClass(typeof(CommandClasses.UserDataClass))]
namespace CommandClasses
{
public class UserDataClass
{
// Specify a key under which we want
// to store our custom data
const string myKey = "AsdkData";
// Define a class for our custom data
public class MyData
{
public int counter;
public MyData()
{
counter = 0;
}
}
[Autodesk.AutoCAD.Runtime.CommandMethod("inc")]
public static void increment()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Hashtable ud = doc.UserData;
MyData md;
md = ud[myKey] as MyData;
if (md == null)
{
object obj = ud[myKey];
if (obj == null)
{
// MyData object not found - first time run
md = new MyData();
ud.Add(myKey, md);
}
else
{
// Found something different instead
ed.WriteMessage(
"Found an object of type \"" +
obj.GetType().ToString() +
"\" instead of MyData.");
}
}
if (md != null)
{
ed.WriteMessage("\nCounter value is: " +
md.counter++);
}
}
[Autodesk.AutoCAD.Runtime.CommandMethod("bogus")]
public static void addBogus()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Hashtable ud = doc.UserData;
Point2d pt = new Point2d();
try
{
ud.Add(myKey, pt);
}
catch
{
ed.WriteMessage(
"\nCould not add bogus object at \"" +
myKey +
"\", must be something there already.");
}
}
}
}
Now let's take a look at that running:
[From first drawing...]
Command: inc
Counter value is: 0
Command: inc
Counter value is: 1
Command: inc
Counter value is: 2
Command: inc
Counter value is: 3
Command: new
[From second drawing...]
Command: inc
Counter value is: 0
Command: inc
Counter value is: 1
Command: inc
Counter value is: 2
Command: bogus
Could not add bogus object at "AsdkData", must be something there already.
Command: new
[From third drawing...]
Command: bogus
Command: inc
Found an object of type "Autodesk.AutoCAD.Geometry.Point2d" instead of MyData.