Autodesk® Revit® 2012 was first release that included a new API called Extensible Storage, which allows API users to store custom data with the Revit model. This blogpost will cover the basics of working with the Extensible Storage API over two posts.
To use the Extensible Storage feature, we first define our own data container, add data to it, and attach its instance to an element in a Revit model. This functionality provides a structured way of handling and storing data.
Defining Schema and Fields
To use the Extensible Storage API, we first need to define a new schema. A schema is a description of the data structure that is stored in a Revit document, and is similar to a Class in object-oriented programming language. A schema contains identity information, documentation, and a list of fields of the data structure.
The SchemaBuilder class is used to create and populate a schema. Any particular Revit model can have more than one schema, and to uniquely identify each schema, GUID is used. The constructor of the SchemaBuilder class accepts this GUID as a parameter. Schema name is another required field; after creating a schema, you must provide a name to the schema, using SchemaBuilder.SetSchemaName() method. The SchemaBuilder class also provides a method to add additional description (called documentation) for the schema.
Once we create an instance of the SchemaBuilder class, we can set the read and write access levels to this schema which help determine who can read or write data into the schema. The schema data can be configured to be used by all users, or by a specific application vendor or by just a specific application from a vendor. If we set the access level to any setting other than Public, we need to set the Vendor Id on the SchemaBuilder class. The following lines of code shows how to create a new SchemaBuilder object, set the access levels and set the name and documentation of the schema that will be created using this SchemaBuilder object:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB.ExtensibleStorage;
namespace ExtensibleStorageSample
{
[Regeneration(RegenerationOption.Manual)]
[Transaction(TransactionMode.Manual)]
public class Command : IExternalCommand
{
Guid schemaGuid = new Guid(
"0DC954AE-ADEF-41c1-8D38-EB5B8465D255");
#region IExternalCommand Members
public Result Execute(
ExternalCommandData commandData,
ref string message, ElementSet elements)
{
// Create Transaction for working with schema
Transaction trans = new Transaction(
commandData.Application.ActiveUIDocument.Document,
"ExtensibleStore");
trans.Start();
// Select a wall
Reference wallRef = commandData.Application.ActiveUIDocument.Selection.PickObject(
Autodesk.Revit.UI.Selection.ObjectType.Element);
Wall wall =
commandData.Application.ActiveUIDocument.Document.GetElement(
wallRef)
as Wall;
// Create a schema builder
SchemaBuilder builder = new SchemaBuilder(schemaGuid);
// Set read and write access levels
builder.SetReadAccessLevel(AccessLevel.Public);
builder.SetWriteAccessLevel(AccessLevel.Public);
// Note: if this was set as vendor or application,
// we would have addtionally required to use SetVendorId
// Set name to this schema builder
builder.SetSchemaName("WallSocketLocation");
builder.SetDocumentation(
"Data store for socket related info in a wall");
trans.Commit();
return Result.Succeeded;
}
#endregion
}
}
Next, we need to create the descriptions of the specific data that will be contained in the schema. These descriptions are called fields. Fields can be defined as the description of a specific data within a Schema and are similar to property of a class in object-oriented programming. Field contains the name, documentation, access control, types, and units. This is used as a key to access corresponding data on a Revit element.
Just like we used SchemaBuilder helper class to create a schema, we use the FieldBuilder helper class to create a new field. This class helps create a template for the fields. The SchemaBuilder object itself provides methods like AddSimpleField() which returns an instance of FieldBuilder class, and the parameters for this method include name and type of data to be stored. Additionally, we can set the description (called documentation), unit types, etc. The following lines of code show how to use FieldBuilder helper class to create a new field and set the name, unit type, etc.
// Create field1
FieldBuilder fieldBuilder1 =
builder.AddSimpleField("SocketLocation", typeof(XYZ));
// Set unit type
fieldBuilder1.SetUnitType(UnitType.UT_Length);
// Add documentation (optional)
// Create field2
FieldBuilder fieldBuilder2 =
builder.AddSimpleField("SocketNumber", typeof(String));
Once the descriptions of specific data (fields) that constitute a schema have been defined in a SchemaBuilder class, we need to get access to the instance of the schema created by this SchemaBuilder object. This is done by registering this object using the SchemaBuilder.Finish() method, as shown below.
// Register the schema object
Schema schema = builder.Finish();
Now that we have created the schema object, we need to create an instance of this schema which will be attached to a Revit element. This instance of the schema object is called Entity. In object-oriented terminology, schema can be thought to be a class while entity is an instance of that class.
Since the entity object is based on the schema that defines it, the constructor of the entity class accepts the schema object. From the schema object, we can directly get access to the fields using the field name and store values in them using the Entity.Set() method.
Once a valid entity object has been created and all the desired data has been added to it, we can use the Element.SetEntity() method available on Element object to attach the entity to the Revit element. The following lines of code show how to create a new entity object and set it on the Wall element.
// Now create entity (object) for this schema (class)
Entity ent = new Entity(schema);
Field socketLocation = schema.GetField("SocketLocation");
ent.Set<XYZ>(socketLocation,
new XYZ(2, 0, 0),
DisplayUnitType.DUT_METERS);
Field socketNumber = schema.GetField("SocketNumber");
ent.Set<String>(socketNumber, "200");
if (null != wall)
{
wall.SetEntity(ent);
}
Please note that only one entity (defined by a given schema) can be attached to an element. If multiple entities from the same schema are attached to an element, only the last Entity is retained. If there is a requirement for adding multiple Entities to an element, we would need to create different entity objects defined from different schemas and attach these different entities to the element.
// Now create another entity (object) for this schema (class)
Entity ent1 = new Entity(schema);
Field socketNumber1 = schema.GetField("SocketNumber");
ent1.Set<String>(socketNumber1, "400");
if (null != wall)
{
wall.SetEntity(ent1);
}
// Note: this will actually replace
// the previous entity on the wall
In the next post on this topic, we shall look at accessing the data that we just stored on the wall element.