Revit二次开发——扩展存储

​  在revitAPI中,提供了Extensible Storage framework,可以使开发者将需要存储的数据存到Revit的rvt文件中, 扩展的数据始终跟着这个Rvt文件走,不会丢失。而且存储的数据可以设置访问权限。对于保密数据可以只能是你自己的程序读取,或只有具有特殊的开发者代号的程序才能访问。对于数据的保密性非常有帮助。

一、相关类

1、SchemaBuilder

  Autodesk.Revit.DB.ExtensibleStorage下

  此类用来创建Schema,相当于一个工厂,可以在此类中进行一些设置,生产出来的Schema遵循这些设置。

public class SchemaBuilder : IDisposable
{//使用guid构造一个SchemaBuilder,生产出来的Schema使用此guidpublic SchemaBuilder(Guid guid);~SchemaBuilder();public bool IsValidObject { get; }//guid是否有效public static bool GUIDIsValid(Guid guid);//生产者id是否有效public static bool VendorIdIsValid(string vendorId);//判断一个字符串是否可以作为SchemaBuilder实例的名字public bool AcceptableName(string name);//添加一个数组属性public FieldBuilder AddArrayField(string fieldName, Type fieldType);//添加一个映射属性public FieldBuilder AddMapField(string fieldName, Type keyType, Type valueType);//添加一个简单属性public FieldBuilder AddSimpleField(string fieldName, Type fieldType);public sealed override void Dispose();//关闭当前工厂并生产一个Schemapublic Schema Finish();//当前工厂是否可用public bool Ready();//设置ApplicationGUID,当AccessLevel为Application时必须要设置public SchemaBuilder SetApplicationGUID(Guid applicationGUID);//设置文档注解public SchemaBuilder SetDocumentation(string documentation);//设置读权限public SchemaBuilder SetReadAccessLevel(AccessLevel readAccessLevel);//设置生产的Schema名字,可用于唯一识别Schemapublic SchemaBuilder SetSchemaName(string schemaName);//设置生产者id。当AccessLevel为Application或者Vender时必须要设置public SchemaBuilder SetVendorId(string vendorId);//设置写权限public SchemaBuilder SetWriteAccessLevel(AccessLevel writeAccessLevel);[HandleProcessCorruptedStateExceptions]protected virtual void Dispose(bool A_0);protected virtual void ReleaseUnmanagedResources(bool disposing);
}

2、AccessLevel

  Autodesk.Revit.DB.ExtensibleStorage下

  这是一个枚举,表示SchemaBuilder/Schema的访问权限,一共有三个值:

public enum AccessLevel
{//所有人都可以访问Public = 1,//只有对象的生产者才可以访问Vendor = 2,//只有创建此对象的应用可以访问Application = 3
}

  注:对于不需要加密的数据,读写操作均使用Public即可。对于需要加密的数据,需要使用Vendor或者Application,但是对于Vendor和Application的理解还不清晰,所以没有测试过,网上也没有找到相关实例,有待后续实践。

3、Schema

  Autodesk.Revit.DB.ExtensibleStorage下,是一个中间产物,可以用它构造Entity,也可以调用静态方法在内存中查找Schema实例。

public class Schema : IDisposable
{//没有构造方法,只能通过工厂类SchemaBuilder创建~Schema();//获取ApplicationGUIDpublic Guid ApplicationGUID { get; }//获取注解public string Documentation { get; }//获取GUIDpublic Guid GUID { get; }public bool IsValidObject { get; }public AccessLevel ReadAccessLevel { get; }public string SchemaName { get; }public string VendorId { get; }public AccessLevel WriteAccessLevel { get; }//从所有打开的文档中删除与此架构对应的所有实体,并从内存中删除此架构。public static void EraseSchemaAndAllEntities(Schema schema, bool overrideWriteAccessWithUserPermission);//列出内存中所有的Schemapublic static IList<Schema> ListSchemas();//根据guid在内存中获取Schemapublic static Schema Lookup(Guid guid);public sealed override void Dispose();//根据属性名获取属性public Field GetField(string name);//列出所有属性public IList<Field> ListFields();//是否有读权限public bool ReadAccessGranted();//是否有写权限public bool WriteAccessGranted();[HandleProcessCorruptedStateExceptions]protected virtual void Dispose(bool A_0);protected virtual void ReleaseUnmanagedResources(bool disposing);
}

4、Entity

  Autodesk.Revit.DB.ExtensibleStorage下

  可以视为应用了Schema的实例,数据的真实载体。SchemaBuilder定义了Schema的访问权限、属性名和类型、Schema的名字、guid等信息,Schema作为一个中间产物,可以用来构造Entity,也可以作为工具类获取内存中的Schema信息。

public class Entity : IDisposable
{public Entity();public Entity(Guid schemaGUID);//使用Schema构造一个Entitypublic Entity(Schema schema);public Entity(Entity other);~Entity();public bool IsValidObject { get; }public Schema Schema { get; }public Guid SchemaGUID { get; }//重置属性值为默认值public void Clear(Field field);//重置属性值为默认值public void Clear(string fieldName);public sealed override void Dispose();//获取属性的类型,FieldType为泛型占位符,表示属性的真实类型public FieldType Get<FieldType>(string fieldName);public FieldType Get<FieldType>(Field field);public FieldType Get<FieldType>(string fieldName, DisplayUnitType displayUnits);public FieldType Get<FieldType>(Field field, DisplayUnitType displayUnits);public bool IsValid();public bool ReadAccessGranted();public bool RecognizedField(Field field);//为给定的属性赋值public void Set<FieldType>(Field field, FieldType value);public void Set<FieldType>(string fieldName, FieldType value);public void Set<FieldType>(Field field, FieldType value, DisplayUnitType displayUnits);public void Set<FieldType>(string fieldName, FieldType value, DisplayUnitType displayUnits);public bool WriteAccessGranted();[HandleProcessCorruptedStateExceptions]protected virtual void Dispose(bool A_0);protected virtual void ReleaseUnmanagedResources(bool disposing);
}

5、DataStorage

  Autodesk.Revit.DB.ExtensibleStorage下

  数据存储类,Element的子类,视为一个元素。在Revit中,所有的Element都拥有SetEntity()方法,可以做扩展数据的载体,但是存储到一般的模型元素上,数据会随着模型的删除而被删除,数据不安全。为了解决这个问题,RevitAPI提供了DataStorage类专门用来做扩展存储。

  一个Revit文档可以创建很多个DataStorage实例,并且实例的名字是可以重复的,如果获取DataStorage实例的时候使用名字作为条件,则可能导致获取到错误实例。因此建议使用自定义的ElementId进行获取,或者按照一定规则给DataStorage实例命名,并在创建之前检查是否已经存在同名的DataStorage实例。

public class DataStorage : Element
{//在指定文档中创建一个DataStorage实例,需要事务支持public static DataStorage Create(Document doc);
}

二、代码实例

1、存储简单数据

[Transaction(TransactionMode.Manual)]
public class StoreSingle: IExternalCommand
{public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements){var uiApp = commandData.Application;var app = uiApp.Application;var uiDoc = uiApp.ActiveUIDocument;var doc = uiDoc.Document;var activeView = uiDoc.ActiveView;var sel = uiDoc.Selection;using (var trans = new Transaction(doc,"BIM")){try{trans.Start();//创建一个数据存储对象DataStorage dataStorage = DataStorage.Create(doc);dataStorage.Name = "MySingleDataStorage";//创建Guidvar buildGuid = Guid.NewGuid();var appGUID = Guid.NewGuid();//创建框架构造器SchemaBuilder builder = new SchemaBuilder(buildGuid);//设置读权限builder.SetReadAccessLevel(AccessLevel.Public);//设置写权限builder.SetWriteAccessLevel(AccessLevel.Public);//设置框架名builder.SetSchemaName("WallSocketLocation");//设置文档说明builder.SetDocumentation("Data store for socket related info in a wall");//创建属性构造器var fieldBuilder1 = builder.AddSimpleField("SocketLocation", typeof(XYZ));//属性值类型fieldBuilder1.SetUnitType(UnitType.UT_Length);var fieldBuilder2 = builder.AddSimpleField("SocketName", typeof(string));//生产框架Schema schema = builder.Finish();//使用框架构造实体Entity entity = new Entity(schema);//为属性赋值//注意:这里获取属性时只能通过schema获取,entity没有获取Filed的方法Field socketLocation = schema.GetField("SocketLocation");entity.Set(socketLocation,new XYZ(2,0,0), DisplayUnitType.DUT_METERS);Field socketName = schema.GetField("SocketName");entity.Set(socketName, dataStorage.Name);//将实体添加到wall中dataStorage.SetEntity(entity);trans.Commit();}catch (Exception e){message += e.Message;return Result.Failed;}}return Result.Succeeded;}
}

2、存储数组/文件

  存储数组和文件的原理一致,因为文件可以转换成字节数组。

  下面的案例实现:将一份文件储存到DataStorage实例中,然后获取到存储的内容,转存到桌面上

[Transaction(TransactionMode.Manual)]
class ArrayStorage : IExternalCommand
{public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements){var uiApp = commandData.Application;var app = uiApp.Application;var uiDoc = uiApp.ActiveUIDocument;var doc = uiDoc.Document;var activeView = uiDoc.ActiveView;var sel = uiDoc.Selection;using (var trans = new Transaction(doc,"StoreArray")){try{trans.Start();//创建数据存储对象DataStorage myDataStorage = DataStorage.Create(doc);//这里使用Name作为筛选dataStorage的条件,要注意命名的唯一性myDataStorage.Name = "myDataStorage";//创建框架构造器SchemaBuilder builder = new SchemaBuilder(Guid.NewGuid());builder.SetReadAccessLevel(AccessLevel.Public);builder.SetWriteAccessLevel(AccessLevel.Public);builder.SetSchemaName("ByteArraySchema");//注意存储数组的时候,FieldType不能给数组类型,而要给元素类型//如果元素类型是一个集合,需要给接口类型,例如IList<T>builder.AddArrayField("ByteArray", typeof(byte));//注册schemaSchema schema = builder.Finish();//创建schema实例entityvar entity = new Entity(schema);//获取属性var field = schema.GetField("ByteArray");//获取数据字节数组,这里读取了一个文件var value = File.ReadAllBytes(@"F:\工作文档\usingt.snippet").ToList();//为属性赋值//为数组属性赋值的时候,不能使用数组,而是使用IList<T>,因为Revit无法识别数组类型entity.Set<IList<byte>>(field, value);//将schema添加到数据储存对象中myDataStorage.SetEntity(entity);//更新模型doc.Regenerate();trans.Commit();}catch (Exception e){message += e.Message;return Result.Failed;}}//读取存储的文件流//使用过滤器获取存储对象FilteredElementCollector collector = new FilteredElementCollector(doc);var dataStorageList = collector.OfClass(typeof(DataStorage)).ToList();var dataStorage = dataStorageList.FirstOrDefault(o => o.Name == "myDataStorage");if (dataStorage != null) {//获取Schemavar schemaGuids = dataStorage.GetEntitySchemaGuids();Schema targetSchema = null;foreach (var schemaGuid in schemaGuids){Schema schema = Schema.Lookup(schemaGuid);if (schema.SchemaName == "ByteArraySchema"){targetSchema = schema;break;}}if (targetSchema == null) {message += "targetSchema is not exist";return Result.Failed;}Entity targetEntity = dataStorage.GetEntity(targetSchema);if (targetEntity == null) {message += "targetEntity is not exist";return Result.Failed;}var byteArray = targetEntity.Get<IList<byte>>("ByteArray").ToArray();File.WriteAllBytes(@"C:\Users\dengbq\Desktop\usingt.snippet", byteArray);}return Result.Succeeded;}
}

  注意:

  Revit扩展存储支持Simple、Array和Map三种形式,但是并不支持所有的数据类型,比如集合、数组、自定义类型等。一些简单类型,比如string、int、byte等基本数据类型是支持的,如果需要存储集合等数据集,无论使用哪种形式,都需要将它们转化成string进行存储,拿到之后再解析成集合。

  比如:使用Array形式存储一系列列表,因为每一个元素都是List类型,而Revit又不支持这种类型,此时就需要将每一个元素按照一定的规则转换成string进行存储,获取的时候再进行解析即可,其余两种形式亦同。

  下面的实例展示Map形式存储集合:

3、存储字典集合

[Transaction(TransactionMode.Manual)]
class MapStorage : IExternalCommand
{public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements){var uiApp = commandData.Application;var app = uiApp.Application;var uiDoc = uiApp.ActiveUIDocument;var doc = uiDoc.Document;var activeView = uiDoc.ActiveView;var sel = uiDoc.Selection;using (var trans = new Transaction(doc, "MapStorage")){try{trans.Start();DataStorage dataStorage = DataStorage.Create(doc);dataStorage.Name = "myMapDataStorage";SchemaBuilder builder = new SchemaBuilder(Guid.NewGuid());builder.SetSchemaName("MySchema");builder.SetReadAccessLevel(AccessLevel.Public);builder.SetWriteAccessLevel(AccessLevel.Public);builder.AddMapField("MyMap", typeof(string), typeof(string));var schema = builder.Finish();Entity entity = new Entity(schema);Field field = schema.GetField("MyMap");IDictionary<string, string> map = new Dictionary<string, string>();List<int> oddNumbers = new List<int>() { 1, 3, 5, 7, 9 };List<int> evenNumbers = new List<int>() { 2, 4, 6, 8, 10 };//将集合数据转换成字符串string oddNumbersStr = string.Join(",", oddNumbers);string evenNumbersStr = string.Join(",", evenNumbers);map.Add("OddNumbers", oddNumbersStr);map.Add("EvenNumbers", evenNumbersStr);entity.Set(field, map);dataStorage.SetEntity(entity);doc.Regenerate();trans.Commit();}catch (Exception e){message += e.Message;return Result.Failed;}//读取数据FilteredElementCollector collector = new FilteredElementCollector(doc);var myDataStorage = collector.OfClass(typeof(DataStorage)).FirstOrDefault(o => o.Name == "myMapDataStorage") as DataStorage;if (myDataStorage != null){//获取Schemavar schemaGuids = myDataStorage.GetEntitySchemaGuids();Schema targetSchema = null;foreach (var schemaGuid in schemaGuids){Schema schema = Schema.Lookup(schemaGuid);if (schema.SchemaName == "MySchema"){targetSchema = schema;break;}}if (targetSchema == null){message += "targetSchema is not exist";return Result.Failed;}Entity targetEntity = myDataStorage.GetEntity(targetSchema);if (targetEntity == null){message += "targetEntity is not exist";return Result.Failed;}var myMap = targetEntity.Get<IDictionary<string, string>>("MyMap");string msg = "";foreach (var item in myMap){var key = item.Key;var value = item.Value;msg += key + "---" + value + "\n";}System.Windows.Forms.MessageBox.Show(msg);}}return Result.Succeeded;}
}

三、总结

1、扩展存储流程

存储数据:

  开启事务—创建DataStorage实例(或者选择某一个元素)—创建SchemaBuilder并进行配置—创建Schema—创建Entity—获取属性并为属性赋值—为DataStorage实例设置Entity

读取数据

  通过过滤器或者id在Document中找到存储数据的元素—使用Schema静态方法在内存中找到Schema—使用获取到的Schema,调用存储元素的GetEntity方法获取Entity实体—调用Entity.Get()方法获取指定属性值。

​ 例如:DataStorage.Name和Schema.Name已知

​ 通过过滤器获取DataStorage实例(或者元素实例)—调用GetEntitySchemaGuids()获取该元素所有的EntitySchemaGuid—遍历EntitySchemaGuids,使用Schema.Lookup()获取Schema—根据Schema.Name找出目标Schema—调用DataStorage.GetEntity获取Entity对象—调用Entity.Get()获取属性值—解析属性值

2、一些注意事项

  1)创建DataStorage后,为了在获取数据时能够准确地找到此实例,一定要为Name或者Id赋一个唯一的值,建议使用Guid,这样就不容易在同一篇文档中存在两个同名/同Id的实例,后面在获取的时候也不会出错。

  2)关于属性定义

  在定义属性的时候,需要给定属性的类型,例如:

  builder.AddSimpleField(“SocketLocation”, typeof(XYZ));

  builder.AddArrayField(“MyArray”, typeof(byte));

  builder.AddMapField(“MyMap”, typeof(string), typeof(string));

  Revit扩展存储支持Simple、Array和Map三种形式,但是并不支持所有的数据类型,比如集合、数组、自定义类型等。一些简单类型,比如string、int、byte等基本数据和RevitAPI自带的一些数据类型是支持的,如果需要存储集合等数据集,无论使用哪种形式,都需要将它们转化成string进行存储,拿到之后再解析成集合。

  比如:使用Array形式存储一系列列表,因为每一个元素都是List类型,而Revit又不支持这种类型,此时就需要将每一个元素按照一定的规则转换成string进行存储,获取的时候再进行解析即可,其余两种形式亦同。

  详见第三个实例。

  3)关于属性赋值

  当我们使用Array或者Map形式做数据储存时,为属性赋值仅支持IList和IDictionary<K,V>,不支持List和Dictionary<K,V>

  这一点和第2)点不矛盾,第2)点讲的是属性的定义,这里讲的是属性的赋值。

Revit二次开发——扩展存储相关推荐

  1. Revit二次开发从入门到精通学习之路, (含Revit二次开发教程下载)

    Revit二次开发从入门到精通学习之路 Autodesk Joe Ye叶雄进 2. 18 2014    yexiongjin@hotmail.com Revit在国内的应用越来越广泛, Revit ...

  2. Revit二次开发——元素信息绑定的两种方法

    <一>通过共享参数与项目参数绑定 在二次开发功能中,很多模型需要添加附加参数,这时候就得使用共享参数或者项目参数来实现,在文档中对于可载入族添加参数时,一般都是为族添加共享参数,因为项目参 ...

  3. C# Revit二次开发基础/核心编程--- Application\Document\Transaction

    一.本节课程 C# Revit二次开发基础/核心编程--- Application\Document\Transaction 二.本节要讲解的知识点 Application\Document\Tran ...

  4. Revit 二次开发前景

    大家也许已经嗅到了Revit发展势头.毫无疑问,Revit是建筑业设计利器.现在很多业主还不太懂设计工具与成本的关系,因为许多的业主是国家,机构等.他们只要人给设计出来,有关系,就可以中标.但是随着体 ...

  5. Revit二次开发2、RevitAPI、RevitAPIUI及Revit开发三大利器

    Revit二次开发2.开发基础之--RevitAPI.RevitAPIUI及Revit开发三大利器 RevitAPI.dll RevitAPIUI.dll Revit开发三大利器 外部命令(IExte ...

  6. Revit二次开发,新手接入IExternalCommand、IExternalApplication,如何使用它们!

    1.首先安装好Revit,至于版本是哪个关系不大.具体怎么安装,就不啰嗦了.安装完成后,桌面会有类似这样的图标: 上图是Revit2020和2019的图标,我自己的电脑是安装了两个版本的Revit. ...

  7. Revit二次开发—参数的读取与写入

    关注公众号及时获取文章更新 注:本文转自公众号:BIMCoder梁老师 一.前言 在Revit二次开发中,参数是非常重要的组成部分,那么我们该如何从构件获取参数并修改该参数. 二.方法 首先得到一个E ...

  8. Revit二次开发有几种方式?做Revit二次开发的必看!

    Revit二次开发有三种方式:外部命令(IExternalCommand).外部应用(IExternalApplication)和宏(Macro).下面将简要说明外部应用.宏的开发过程,并着重说明外部 ...

  9. Revit二次开发笔记

    Revit二次开发笔记: 配置:以revit 2021为例 Visual studio中的配置 以下是如果要翻成EnergyPlus模型需要的配置 如何安装Addin Manger和lookup插件 ...

最新文章

  1. LINUX的20练习题
  2. DeepMind大招,以视觉为媒介,做无监督机器翻译,效果极好
  3. 50代码HTML5 Canvas 3D 编辑器优雅搞定
  4. 查询各个年级的平均分_二年级数学:期中考试前精心准备一下,会有意想不到的收获哦!...
  5. 错误处理:Syntax error: Bad for loop variable
  6. jinja2 {{}} href 双大括号
  7. 未来的计算机作文2000字,未来的模样2000字作文
  8. 学习总结:机器学习(六)
  9. Spark:使用partitionColumn选项读取数据库原理
  10. An Easy Problem(信息学奥赛一本通-T1223)
  11. mysql shell无法启动服务_[shell脚本] mysql服务启动脚本
  12. Android如何通过shareduserid获取系统权限
  13. java中arraylist扩容问题_Arraylist扩容机制
  14. 中国各省市恩格尔系数表(​1978-2019年)
  15. 微信小程序实现垂直tab标签页的切换及动态的选中下划线移动-纵向
  16. docker下安装wekan看板工具
  17. 你必须认识的五名网络女红人
  18. 东方幻想乡/BadApple!!
  19. linux基础(五)----linux命令系统学习----系统管理命令
  20. 风场可视化与原理剖析

热门文章

  1. api接口文档编辑工具-YApi安装部署(window版)
  2. 开源数字基础设施 项目 -- Speckle
  3. 编码器-解码器入门级理解
  4. html实现自动清理js、css文件的缓存
  5. 使用gitbash创建一个翻译词典
  6. 如何提高查找资料能力
  7. 写给屌丝程序员的免费空间个人blog的建站攻略
  8. Kelvin四线连接电阻测试技术及应用
  9. 信用卡客户风险分析与评价
  10. 2021 年最新的个人录制的前后端真正的免费编程学习视频