原文链接:
Extensible Storage of a Map
我在前面的博文里已经介绍过 Revit 2012 API 中新引入的可扩展存储功能 extensible storage。你还可以在 DevHelp Online 和 Revit 2012 API webcast 找到更多的细节。
最近我更新了网络广播上的例程,下面是最新的主函数代码:
我在前面的博文里已经介绍过 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。