Механизм Extensible Storage в Revit API позволяет записывать и хранить информацию прямо в элементах модели. Это удобнее, чем хранить информацию где-то отдельно — не потеряется. А по сравнению в записью в параметры проекта или семейства — пользователь напрямую эту информацию не увидит и не сможет так просто поменять.
Но работа с этим «Хранилищем» организована не очень естественным образом, поэтому и написана данная заметка.
Сохранять информацию можно практически в любом элементе модели.
Для работы необходимо подключить пространство имен Autodesk.Revit.DB.ExtensibleStorage.
Посмотреть сохраненную в элементе информацию можно через RevitLookup.
1. Создаем описание хранилища
SchemaBuilder sb = new SchemaBuilder(new Guid(schemaGuid));
sb.SetReadAccessLevel(AccessLevel.Public);
Далее создаем поля для хранилища, опять же через промежуточный класс FieldBuilder. Изменить количество, тип, имя полей после создания не получится, надо продумать это заранее. Поля могут хранить типы string, int, double и т.д.
Также есть возможность создать поля-списки, но я не очень разобрался, как они работают.
Создадим два простых поля:
FieldBuilder fbName = sb.AddSimpleField(«Username», typeof(string));
FieldBuilder fbAge = sb.AddSimpleField(«UserAge», typeof(int));
2. Выбираем элемент для хранения
Для того, чтобы работать с элементом, нужно получить его из документа. Рекомендую сделать для этой операции отдельный приватный метод.
Например, можно записать в ProjectInfo:
private Element GetStorageElement(Document doc)
{
ProjectInfo pi = doc.ProjectInformation;
return pi as Element;
}
В выбранный элемент:
private Element GetStorageElement(UIDocument uiDoc)
{
Document doc = uiDoc.Document;
Element elem = doc.GetElement(uiDoc.Selection.GetElementIds().First());
return elem;
}
Или вообще во что-то странное, например, в организацию браузера:
private Element GetStorageElement(Document doc)
{
BrowserOrganization bo = new FilteredElementCollector(doc)
.OfClass(typeof(BrowserOrganization))
.Cast<BrowserOrganization>()
.First();
return bo as Element;
}
Дальнейшая работа не зависит от того, в каком именно элементе мы сохраняем информацию.
3. Записываем хранилище в элемент
{
t.Start(«Create storage»);
}
Считываем информацию
{
Element elem = this.GetStorageElement(doc);
Schema sch = Schema.Lookup(new Guid(schemaGuid));
Entity ent = elem.GetEntity(sch);Field f = sch.GetField(«UserAge»);
int age = ent.Get<int>(fr);
using(Transaction t = new Transaction(doc))
{
t.Start(«Revision update»);
ent.Set<int>(fr, famRev + 1);
elem.SetEntity(ent);
t.Commit();
}
}
Проверяем наличие Extensible Storage
В случае, если вы планируете хранить с помощью Extensible Storage какие-то настройки — есть задача проверить, есть ли уже настройки в элементе. Задача усложняется тем, что:
- если в этом файле еще ни разу нчиего не записывалось под нужным GUID — вы получите исключение при попытке вызвать Schema.Lookup;
- Если GUID уже использовался для других элементов — вы сможете получить Schema и Entity, но Entity будет «пустой».
Предлагаю использовать такую конструкцию:
private bool CheckStorageExists(Element elem, string sGuid)
{
try
{
Schema sch = Schema.Lookup(new Guid(sGuid));
Entity ent = elem.GetEntity(sch);
if (ent.Schema != null) return true;
}
catch { }
return false;
}
В этом случае:
- Если Schema еще ни разу не создавалась — получим исключение, выйдем из блока try и код вернет false — «Хранилище отсутствует»;
- Если Schema уже создавался, но для других элементов — Entity будет создан, но свойство Entity.Schema будет равно null. Код выйдет из блока try и вернет false;
- Если Schema создавался и был записан в элемент — свойство Entity.Schema не будет равно null. Код вернет true — «Хранилище присутствует».