This is common request: identify what have changed on Modified event of entities. In fact this feature is not available on the API, mainly because it can potentially represent a big use of memory. But a (partial) workaround may help in some cases!
The main thing is that is not easy to track everything that change on a entity, mainly because one can refer to another, or even contain internal data not easily exposed. Also, in complex scenarios like this, a generic approach may not capture everything.
So I decided to do a quick try with .NET properties, basically using reflection mechanism to store the data and compare back. Below is a common code that track changes with Modified event, but it has a few extra lines (marked in yellow) enabled by .NET Extension mechanism.
[CommandMethod("monitorEntity")]
public void CmdMonitorEntity()
{
// select an entity
Editor ed = Application.DocumentManager.
MdiActiveDocument.Editor;
PromptEntityResult resEnt = ed.GetEntity(
"Select an entity: ");
if (resEnt.Status != PromptStatus.OK) return;
// and register the event
MonitorEntity(resEnt.ObjectId);
}
private void MonitorEntity(ObjectId objectId)
{
Database db = Application.DocumentManager.
MdiActiveDocument.Database;
using (Transaction trans = db.
TransactionManager.StartTransaction())
{
Entity ent = trans.GetObject(objectId,
OpenMode.ForRead) as Entity;
// start the monitor this object
ent.MonitorPropertyChanges();
// as usual, register for 'Modified' event
ent.Modified += new EventHandler(ent_Modified);
}
}
void ent_Modified(object sender, EventArgs e)
{
Entity ent = sender as Entity;
if (ent == null) return;
// list of what changed
string[] propsModified = ent.GetModifiedProperties();
if (propsModified.Length == 0) return;
Editor ed = Application.DocumentManager.
MdiActiveDocument.Editor;
// write the values that changed
ed.WriteMessage("\nProperties were modified:");
foreach (string propName in propsModified)
{
ed.WriteMessage("\n{0}, previous value {1}",
propName, ent.GetPreviousValue(propName));
}
}
The code above should look very common, no extra feature other than those in yellow. As a side note, that is the beauty of .NET Extension: we can add new methods for certain types of object and the syntax is quite sharp.
At the image below, note how the properties changed are tracked after the user change the layer using the Property palette. Please test this and make sure the properties you’re tracking are listed with this mechanism. Things like nodes of a polyline will not appear.
Ok, to enable this extension methods, simply create a new class and mark as ‘static’ in C# or create as a ‘Module’ on VB.NET (where an additional metadata is required: <Extension()>).
public static class DBObjectMonitor
{
private static Dictionary<ObjectId,
Dictionary<string, object>> _monitorList;
// list of properties for objects monitored
private static Dictionary<ObjectId,
Dictionary<string, object>> MonitorList
{
get
{
if (_monitorList == null) _monitorList =
new Dictionary<ObjectId,
Dictionary<string, object>>();
return _monitorList;
}
}
/// <summary>
/// Prepare the object to monitor what have changed
/// </summary>
public static void MonitorPropertyChanges(this DBObject dbObj)
{
dbObj.OpenedForModify +=
new EventHandler(dbObj_OpenedForModify);
}
private static void dbObj_OpenedForModify(
object sender, EventArgs e)
{
StorePropeties((DBObject)sender);
}
private static void StorePropeties(DBObject dbObj)
{
Dictionary<string, object> objPropBefore =
new Dictionary<string, object>();
System.Reflection.PropertyInfo[] props =
dbObj.GetType().GetProperties();
foreach (System.Reflection.PropertyInfo prop in props)
{
try
{
// if we cannot write,
// let's not monitor it
if (!prop.CanWrite) continue;
// save the value of the propery
objPropBefore.Add(prop.Name,
prop.GetValue(dbObj, null));
}
catch
{
// get the value through reflection
// cannot be safe...
}
}
// store all the properties based
// on the objectId
MonitorList.Remove(dbObj.ObjectId);
MonitorList.Add(dbObj.ObjectId, objPropBefore);
}
/// <summary>
/// List of properties changed on the Modified event
/// </summary>
public static string[]
GetModifiedProperties(this DBObject dbObj)
{
// is on the list?
if (!MonitorList.ContainsKey(dbObj.ObjectId))
return null;
// get the list of values
Dictionary<string, object> objPropBefore =
MonitorList[dbObj.ObjectId];
// this will store the name of what was changed
List<String> propList = new List<String>();
// list of properties
System.Reflection.PropertyInfo[] props =
dbObj.GetType().GetProperties();
foreach (System.Reflection.PropertyInfo prop in props)
{
try
{
// if we cannot write,
// let's not monitor it
if (!prop.CanWrite) continue;
// save the value of the propery
object valueBefore = objPropBefore[prop.Name];
object valueAfter = prop.GetValue(dbObj, null);
if (valueBefore.Equals(valueAfter))
// remove from the original list
objPropBefore.Remove(prop.Name);
else
propList.Add(prop.Name);
}
catch
{
// get the value through reflection
// may not be safe...
}
}
return propList.ToArray<string>();
}
/// <summary>
/// Get the value for the property
/// before this current change
/// </summary>
/// <param name="propertyName">
/// Name of the property</param>
/// <returns>The previous value</returns>
public static object
GetPreviousValue(
this DBObject dbObj,
string propertyName)
{
// get the list
Dictionary<string, object> objPropBefore =
MonitorList[dbObj.ObjectId];
// this property exist?
if (!objPropBefore.ContainsKey(propertyName))
return null;
return objPropBefore[propertyName];
}
}