The Revit API 2012 provides an official way to extend the Element data now. In the old days, we could only create some cumbersome invisible parameters and attach them to the elements where we want to store some extra data. Now the Extensible Storage API can directly address so basic a need for us and has much power.
However, as introduced and demonstrated before, unnecessary complexities, impositions, redundancies and inconsistencies are here and there. In case any of those points as mentioned repeatedly slip out of our minds, the data storage or read back would just fail. In addition, the out of box supported data types are just a few, far less than enough.
Fortunately, we can create something to get all the inconveniences away and make life easier and better. As demonstrated previously, the super easy and cool Revit Data Operator is the choice. Various data types have already been demonstrated there. In this article, let us see how a Dictionary like List can be handled by the same Revit Data Operator.
Here is the test code to attach a Dictionary like List data to an Element:
Reference picked = CachedUiApp.ActiveUIDocument.Selection.PickObject(ObjectType.Element, "Pick a window to attach our data");
using (Transaction trans = new Transaction(CachedDoc, "AttachDataTo"))
{
trans.Start();
MyData3 data = new MyData3
{
idToData = new List<MyKeyValuePair<string, MyData>>()
{
new MyKeyValuePair<string, MyData>( "AAAA",
new MyData
{
byteVar = 155,
charVar = 'a',
ushortVar = 15535,
longVar = 1234567890,
decimalVar = 1.123456789123456789M,
dateTimeVar = new DateTime(2011, 2, 14, 23, 59, 59)
}
),
new MyKeyValuePair<string, MyData>( "BBBB",
new MyData
{
byteVar = 255,
charVar = 'b',
ushortVar = 25535,
longVar = 2234567890,
decimalVar = 2.123456789123456789M,
dateTimeVar = new DateTime(2012, 2, 14, 23, 59, 59)
}
),
new MyKeyValuePair<string, MyData>( "CCCC",
new MyData
{
byteVar = 55,
charVar = 'c',
ushortVar = 35535,
longVar = 3234567890,
decimalVar = 3.123456789123456789M,
dateTimeVar = new DateTime(2013, 2, 14, 23, 59, 59)
}
),
}
};
RevitAddinWizard.SuperEasyCoolDataOperator.SetDataToElement(
"DA4AAE5A-4EE1-45A8-B3E8-F790C84CC44F", data, CachedDoc.GetElement(picked));
trans.Commit();
}
"Pick a window to attach our data");
using (Transaction trans = new Transaction(CachedDoc, "AttachDataTo"))
{
trans.Start();
MyData2 data = new MyData2
{
strList = new List<string> { "User1", "User2", "User3" }
};
RevitAddinWizard.SuperEasyCoolDataOperator.SetDataToElement(
"DA4AAE5A-4EE1-45A8-B3E8-F790C84CC44F", data, CachedDoc.GetElement(picked));
trans.Commit();
}
Here is the test code to read the same Dictionary like List data back from the same Element:
Reference picked = CachedUiApp.ActiveUIDocument.Selection.PickObject(ObjectType.Element, "Pick the window having our data");
using (Transaction trans = new Transaction(CachedDoc, "ReadDataFrom"))
{
trans.Start();
MyData3 dataStore = RevitAddinWizard.SuperEasyCoolDataOperator.GetDataFromElement<MyData3>("DA4AAE5A-4EE1-45A8-B3E8-F790C84CC44F", CachedDoc.GetElement(picked));
string str = string.Empty;
for (int i = 0; i < dataStore.idToData.Count; i++)
{
str += string.Format("Data #{0}:\n", i+1);
MyData data = dataStore.idToData[i].Value;
FieldInfo[] propInfoArrary = typeof(MyData).GetFields();
foreach (FieldInfo fi in propInfoArrary)
{
object obj = typeof(MyData).InvokeMember(fi.Name, BindingFlags.GetField, null, data, null);
str += string.Format("\t{0} : {1}\n", fi.Name, obj == null ? string.Empty : obj.ToString());
}
}
MessageBox.Show(str, "MyData");
trans.Commit();
}
Here is the new data class:
public class MyData
{
public byte byteVar;
public ushort ushortVar;
public long longVar;
public char charVar;
public decimal decimalVar;
public DateTime dateTimeVar;
}
public class MyKeyValuePair<K, V>
{
public MyKeyValuePair() { }
public MyKeyValuePair(K k, V v)
{
Key = k;
Value = v;
}
public K Key { get; set; }
public V Value { get; set; }
}
public class MyData3
{
public List<MyKeyValuePair<string, MyData>> idToData;
}
Here is the core of the Easy & Cool Revit Data Operator:
using System;
using System.Xml.Serialization;
using System.IO;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage; //Revit API 2012 only
namespace RevitAddinWizard
{
public class SuperEasyCoolDataOperator
{
private const string SingleStringFiledName = "SingleStringForAll";
public static void SetDataToElement(string id, object o, Element e)
{
SchemaBuilder sb = new SchemaBuilder(new Guid(id));
sb.SetSchemaName(id.Replace("-", ""));
FieldBuilder fb = sb.AddSimpleField(SingleStringFiledName, typeof(string));
XmlSerializer xml = new XmlSerializer(o.GetType());
using (StringWriter w = new StringWriter())
{
xml.Serialize(w, o);
Entity ent = new Entity(sb.Finish());
ent.Set<string>(SingleStringFiledName, w.ToString());
e.SetEntity(ent);
}
}
public static T GetDataFromElement<T>(string id, Element e)
{
Schema sch = Schema.Lookup(new Guid(id));
string s = e.GetEntity(sch).Get<string>(SingleStringFiledName);
XmlSerializer xml = new XmlSerializer(typeof(T));
using (StringReader r = new StringReader(s))
{
return (T)xml.Deserialize(r);
}
}
}
}
As can be seen, those complexities and impositions all go away. We do not have to specify the same schema GUIDs, scheme names, field names, data types, unit types repeatedly. We do not have to care about all those intermediate objects such as Schema, SchemaBuilder, Field, FieldBuilder, or Entity anymore either.
What we need to care about now are what we really want, the data object, the Element of concern, and the Schema identifier. If they are consistent with each other, it will be very hard to make the data setting or getting fail.
The data can be as simple as a char or complex like a Dictionary like List.
Life can be so easy like this, huh?
The Revit Addin Wizard (RevitAddinWizard) is going to provide a coder to help generate Extensible Storage code.
Recent Comments