public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Document doc = uidoc.Document; try { // pick an element and define the XYZ // data to store at the same time Reference r = uidoc.Selection.PickObject( ObjectType.Face, new WallFilter(), "Please pick a wall at a point on one of its faces" ); Wall wall = doc.get_Element( r.ElementId ) as Wall; XYZ dataToStore = r.GlobalPoint; Transaction t = new Transaction( doc, "Create Extensible Storage Schemata and Store Data" ); t.Start(); // store the data, and also // demonstrate reading it back StoreDataInWall( wall, dataToStore ); t.Commit(); // list all schemas in memory across all documents ListSchemas(); return Result.Succeeded; } catch( Exception ex ) { message = ex.Message; return Result.Failed; } }这个外部命令首先要求用户通过指定点来选择一面墙体。PickObject 方法可以同时返回选中的墙体和选中的点。它使用一个实现了 ISelectionFilter 接口的 WallFilter 对象作为参数,来限制用户只能选择墙体。
class WallFilter : ISelectionFilter { public bool AllowElement( Element e ) { return e is Wall; } public bool AllowReference( Reference r, XYZ p ) { return true; } }选中的点被封装为一个可扩展存储样式,然后保存到墙体模型中。下面是实现封装的辅助函数:
/// <summary> /// Create an extensible storage schema, /// attach it to a wall, populate it with data, /// and retrieve the data back from the wall. /// </summary> void StoreDataInWall( Wall wall, XYZ dataToStore ) { SchemaBuilder schemaBuilder = new SchemaBuilder( new Guid( "720080CB-DA99-40DC-9415-E53F280AA1F0" ) ); // allow anyone to read the object schemaBuilder.SetReadAccessLevel( AccessLevel.Public ); // restrict writing to this vendor only schemaBuilder.SetWriteAccessLevel( AccessLevel.Vendor ); // required because of restricted write-access schemaBuilder.SetVendorId( "ADNP" ); // create a field to store an XYZ FieldBuilder fieldBuilder = schemaBuilder .AddSimpleField( "WireSpliceLocation", typeof( XYZ ) ); fieldBuilder.SetUnitType( UnitType.UT_Length ); fieldBuilder.SetDocumentation( "A stored " + "location value representing a wiring " + "splice in a wall." ); schemaBuilder.SetSchemaName( "WireSpliceLocation" ); // register the schema Schema schema = schemaBuilder.Finish(); // create an entity (object) for this schema (class) Entity entity = new Entity( schema ); // get the field from the schema Field fieldSpliceLocation = schema.GetField( "WireSpliceLocation" ); // set the value for this entity entity.Set<XYZ>( fieldSpliceLocation, dataToStore, DisplayUnitType.DUT_METERS ); // store the entity on the element wall.SetEntity( entity ); // read back the data from the wall Entity retrievedEntity = wall.GetEntity( schema ); XYZ retrievedData = retrievedEntity.Get<XYZ>( schema.GetField( "WireSpliceLocation" ), DisplayUnitType.DUT_METERS ); }上面的主函数还调用了另外一个辅助函数 ListSchemas 来显示所有被加载到当前文档中的样式。
/// <summary> /// List all schemas in Revit memory across all documents. /// </summary> void ListSchemas() { IList<Schema> schemas = Schema.ListSchemas(); int n = schemas.Count; Debug.Print( string.Format( "{0} schema{1} defined:", n, PluralSuffix( n ) ) ); foreach( Schema s in schemas ) { IList<Field> fields = s.ListFields(); n = fields.Count; Debug.Print( string.Format( "Schema '{0}' has {1} field{2}:", s.SchemaName, n, PluralSuffix( n ) ) ); foreach( Field f in fields ) { Debug.Print( string.Format( "Field '{0}' has value type {1}" + " and unit type {2}", f.FieldName, f.ValueType, f.UnitType ) ); } } }
在上面的例子里,我们只保存了一个简单的 XYZ 数据。最近我被问及如何保存一个更加复杂的数据,比方说字典。
我想使用以下代码将一个字符串字典保存到文档中:
FieldBuilder.AddMapField( MyMappedField, typeof( string ), typeof( string ) );
但问题是我应该如何调用 Entity.Set<???>(???) 方法呢?类似地问题还有,如何读取和删除字典类型的数据呢?
// Note that we use IDictionary<> for // map types and IList<> for array types mySchemaWrapper .AddField<IDictionary<string, string>>( map0Name, UnitType.UT_Undefined, null ); mySchemaWrapper .AddField<IList<bool>>( array0Name, UnitType.UT_Undefined, null );我更新了之前提到的网络广播的例程,最新的主函数如下:
/// <summary> /// Create an extensible storage schema specifying /// a dictionary mapping keys to values, both using /// strings, populate it with data, attach it to the /// given element, and retrieve the data back again. /// </summary> void StoreStringMapInElement( Element e ) { SchemaBuilder schemaBuilder = new SchemaBuilder( new Guid( "F1697E22-9338-4A5C-8317-5B6EE088ECB4" ) ); // allow anyone to read or write the object schemaBuilder.SetReadAccessLevel( AccessLevel.Public ); schemaBuilder.SetWriteAccessLevel( AccessLevel.Public ); // create a field to store a string map FieldBuilder fieldBuilder = schemaBuilder.AddMapField( "StringMap", typeof( string ), typeof( string ) ); fieldBuilder.SetDocumentation( "A string map for Tobias." ); schemaBuilder.SetSchemaName( "TobiasStringMap" ); // register the schema Schema schema = schemaBuilder.Finish(); // create an entity (object) for this schema (class) Entity entity = new Entity( schema ); // get the field from the schema Field field = schema.GetField( "StringMap" ); // set the value for this entity IDictionary<string, string> stringMap = new Dictionary<string, string>(); stringMap.Add( "key1", "value1" ); stringMap.Add( "key2", "value2" ); entity.Set<IDictionary<string, string>>( field, stringMap ); // store the entity on the element e.SetEntity( entity ); // read back the data from the wall Entity retrievedEntity = e.GetEntity( schema ); IDictionary<string, string> stringMap2 = retrievedEntity .Get<IDictionary<string, string>>( schema.GetField( "StringMap" ) ); }你也可以从这里下载完整的代码 ExtensibleStorage.zip。