Building Coder(Revit 二次开发) - 可扩展存储处理字典

来源:互联网 发布:东三省十大网络神曲 编辑:程序博客网 时间:2024/05/15 00:16
原文链接:Extensible Storage of a Map

我在前面的博文里已经介绍过 Revit 2012 API 中新引入的可扩展存储功能 extensible storage。你还可以在 DevHelp Online 和 Revit 2012 API webcast 找到更多的细节。

最近我更新了网络广播上的例程,下面是最新的主函数代码:
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<???>(???) 方法呢?类似地问题还有,如何读取和删除字典类型的数据呢?

回答

简单地说,应该使用 IDictionary<> 作为类型参数调用 Entity.Set<>() 方法,即使实际的数据类型是 Dictionary<>。实际上 Revit API 的帮助文档提到了这一点,不过叙述得不太清楚。顺便说一句,SDK 例程 ExtensibleStorageManager 里也有实际的代码。
  // 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