Per-document data in AutoCAD .NET applications - Part 2

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.

15 responses to “Per-document data in AutoCAD .NET applications - Part 2”

  1. Thanks for your great article,Kean.
    For C++,you give the Falsely MDI-Aware Version example.
    What about .NET?

  2. Thanks, csharpbird.
    If you take a look at the previous entry (Part 1), you can see the "glob" command implementation... this could be considered a "Falsely MDI-Aware" command, if the command was supposed to be considering the counter variable to be local for each document.

  3. Alexander Rivlis Avatar
    Alexander Rivlis

    Thanks, Kean! Your's last three articles is very useful!

  4. Fernando Malard Avatar

    Great couple of articles Kean.
    This is always a subject developers are unaware of when developing ObjectARX applications and it would happen with .NET too.

    Thanks, Fernando.

  5. hi kean,

    are you aware of the difference between static and instance variables in an AutoCAD .NET application command class? If you create static variables (and a static command method), they exist only once. If you create instance variables (and a non-static command method), a new instance of the class is instantiated for each AutoCAD document. This automatically generates and manages per-document data, with no need for any work at all from your side. I have a nice sample demonstrating this, if you like.

    cheers

    jeremy

  6. Kean Walmsley Avatar

    Hi Jeremy,

    Yes, I am - the previous post covered that.

    The reason I used this approach in another recent post is actually that sometimes the class instances you receive automatically are not the ones you expect - particularly when implementing event handlers (presumably it's because you're in the application context rather than being in a particular document's context, depending). So I actually find UserData to be more predictable and therefore dependable.

    Cheers,

    Kean

  7. Hi,Kean
    You've said
    "I've written some code to demonstrate how you might store and access per-document data in the UserData property"
    Where can I find these code? Thank u.

    hhhwjb

  8. Kean Walmsley Avatar

    hhhwjb - it's the C# code in this post.

    Kean

  9. Hi,Kean
    Thank u for ur answer.
    I test this code, when I close the document,the UserData lost. How can I save the UserData together with my documentation?
    Thank u in advance.

  10. Kean Walmsley Avatar

    This example does not persist data in the DWG file: it just associates it with the document for runtime access. If you need to persist data then you'll probably want to use XRecords to do so (search this blog to find examples).

    Kean

  11. Thank u very much.

  12. Hi Kean,
    I came across this article when i was searching for a way to store custom classes in AutoCAD drawing.
    I thought i found the answear until i realised, that this data is not persistent.
    On the other hand XRecord can only store ResultBuffer type in his Data property and DBDictionary can only store classes derived from the DBObject class. Or am I wrong?

    What is the best way to store custom classes id drawings persistently?

    Matus

  13. Hi Matus,

    Regarding your last question (which may also respond to your others), It depends:

    Xdata for smallish amounts (meaning up to about 4K, as a rule of thumb... there's a shared 16K limit per chunk of Xdata, if I recall correctly).

    Xrecords in extension dictionaries (or centrally in the named objects dictionary) for larger amounts.

    ResultBuffers are actually reasonably flexible, if you implement them properly.

    Regards,

    Kean

  14. Thanks Kean, it is still relevant in 2012.
    Regards
    Craig

  15. thank you - this is quite useful. didn't know you can store actual classes in the hash table like that - could come in very handy.

Leave a Reply

Your email address will not be published. Required fields are marked *