In yesterday’s post, we looked at a mechanism that has been introduced in AutoCAD 2015 to simplify associating custom data with a Document. In today’s post we’re going to swap out the custom manager class to make use of the standard UserData mechanism, showing how the PerDocumentClass attribute and the UserData property are actually highly complementary.
Here’s the updated C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
// The all-important attribute telling AutoCAD to instantiate
// the PerDocData class for each open or new document
[assembly: PerDocumentClass(typeof(PerDocSample.PerDocData))]
namespace PerDocSample
{
public class Commands
{
// A simple command to write the contents of our per-document
// data to the command-line
[CommandMethod("PPDD")]
public void PrintPerDocData()
{
var doc = Application.DocumentManager.MdiActiveDocument;
var perDocData = doc.UserData["TtifData"] as PerDocData;
doc.Editor.WriteMessage(
perDocData == null ?
"\nNo user data found." :
perDocData.OpenDateTime.ToString()
);
}
}
// Our per-document data class. This will get instanciated for
// each existing or new document: we get the creation
// notification via either the static Create() method or via
// the public constructor that takes a Document argument
public class PerDocData
{
// We will store the time the document was opened or created
private DateTime _openDateTime;
// Provide a public read-only property for that
public DateTime OpenDateTime
{
get { return _openDateTime; }
}
// Public constructor taking a Document
public PerDocData(Document doc)
{
_openDateTime = DateTime.Now;
doc.UserData.Add("TtifData", this);
}
// Static Create method: this is the first approach tried
// (to differentiate we're adding an hour to the current
// time, so it's clear this method is being called)
public static PerDocData Create(Document doc)
{
var pdd = new PerDocData(doc);
pdd._openDateTime += TimeSpan.FromHours(1);
return pdd;
}
}
}
You’ll see that we’ve simplified the code quite a bit by using UserData, which is really just a per-document map that associates custom application data with an application ID. As the map disappears when the owning Document gets destroyed we no longer have to worry about removing data from a central map as we did last time. And as mentioned last time, Dispose() will be called on any objects implementing it, simplifying the work needed to clean up after ourselves.
For simplicity’s sake I’ve chosen to hardcode the application ID (“TtifData”) in a couple of places in the above code. It’s clearly better to have this defined centrally, but I’ve left that as an exercise for the user (it’s being used across two separate classes, which complicates things slightly: obvious ways to share the definition would be via a common “utils” class or even to combine the two classes into one containing a single definition of the constant).
The code should work exactly as the code in the last post did: try loading it into AutoCAD and running the PPDD command in a few different documents.
It’s simple and works well: hopefully it’s clear that PerDocumentClass in combination with UserData greatly simplifies the work needed to associate data at a per-document level in AutoCAD.