API для работы с семействами. Типоразмеры вложенного семейства, поиск типоразмеров и экземпляров семейства
Приветствую вас в третьей, заключительно части серии статей посвященных API для работы с семействами в проекте.
В последней части мы рассмотрим следующие темы:
- Поиск определенных типоразмеров в проекте
- Поиск определенных экземпляров конкретного семейства
- Отображение типоразмеров семейства Дверь
- Изменение типоразмера вложенного семейства.
Основная функциональность работы с семействами была представлена Стивеном Кэмпбелом (Steven Campbell) в его докладе Ключевые концепции при работе с семействами на Revit DevCamp в Москве. Предыдущие статьи вы можете прочитать по следующим ссылкам: первая часть, вторая часть.
В предыдущей статье мы обсудили как программно обработать выбор семейства или любого другого элемента в интерфейсе Revit. Но что если вам нужно автоматом выбрать и изменить все экземпляры определенного типоразмера?
Время идет, семейства, с которыми нам необходимо работать, становятся все сложнее. Сегодня мы будем работать с кухонными шкафами, в которых есть вложенные семейства, которые представляют собой различные варианты панелей дверей шкафов.
В этом примере мы будем менять тип двери, т.е. будем изменять типоразмер вложенного семейства.
Последняя команды в нашей серии статей по API семейств называется CmdKitchenUpdate. Ее необходимо запустить в модели, представляющей собой дизайн кухни. В ней определяются все возможные типы панелей дверей для шкафов, автоматически выбираются все кухонные шкафы, отображается текущий тип панели, предлагается выбрать новый и применяется выбранный тип для всех шкафов.
Поиск определенных типоразмеров
Первым делом нам необходимо найти все возможные типоразмеры дверей шкафов.
Как всегда, воспользуемся для этих целей классом FilteredElementCollector. В нашем случае нам нужно выбрать все типоразмеры (класс FamilySymbol) категории Обобщенные модели. Для того чтобы определить какие из них являются панелями дверей шкафов, мы воспользуемся LINQ и выберем только те элементы, название которых начинается с «Door panel - » (Панель двери). Код выглядит вот так:
- // Выбор всех панелей дверей шкафов.
- List<Element> door_panel_types = new List<Element>(
- new FilteredElementCollector( doc )
- .OfCategory( BuiltInCategory.OST_GenericModel )
- .OfClass( typeof( FamilySymbol ) )
- .Where<Element>( e => e.Name.StartsWith(
- "Door panel - " ) ) );
Поиск экземпляров семейств
Так как нам нужно изменить панели дверей для всей кухни, то нам нужно сначала выбрать все кухонные шкафы, которые есть в проекте.
В очередной раз воспользуется Filtered ElementCollector. Для фильтрации всех экземпляров шкафов нам нужно применить два «быстрых» фильтра.
Можно конечно применить и большее количество фильтров, и я обязательно это сделаю, если буду работать с большой моделью. Пока же для примера ограничимся двумя.
В качестве последней проверки я удостоверюсь, что у элемента действительно существует параметр «Тип панели двери», который я хочу изменить.
В большой и сложной модели, для достижения оптимальной производительности, такою проверку необходимо сделать с помощью фильтрации по параметру. В нашем тестовом примере я воспользуюсь LINQ.
- // Поиск всех кухонных шкафов
- // у которых есть параметр Door Panel Type («Тип панели двери»)
- IEnumerable<Element> casework
- = new FilteredElementCollector( doc )
- .OfCategory( BuiltInCategory.OST_Casework ) // Шкафы
- .OfClass( typeof( FamilyInstance ) )
- .Where<Element>( e =>
- (null != e.get_Parameter(
- " Door Panel Type" )) );
Отображение доступных панелей
Поиск всех возможных типов панелей и выбор всех шкафов в проекте по сути является главной задачей команды.
Остались достаточно простые шаги:
- Отобразить пользователю текущий тип панели, или «НЕСКОЛЬКО», если среди выбранных шкафов используются различные типы панелей дверей.
- Отобразить список доступных типов панелей дверей и предложить пользователю выбрать один из них.
- Применить выбранный тип для всех шкафов.
Для решения первых двух задач мы воспользуемся обычной формой .NET.
Форма содержит список всех типов панелей дверей, которые мы передали в конструктор формы, и отображает эти типы пользователю в раскрывающемся списке. Также отображается текущий тип панели.
Получить выбранный тип дверей достаточно просто:
- public DoorPanelTypeSelectorForm(
- string current_door_panel_type_name,
- IEnumerable<Element> door_panel_types )
- {
- InitializeComponent();
- label2.Text = current_door_panel_type_name;
- comboBox1.DataSource = door_panel_types;
- comboBox1.DisplayMember = "Name"; // Свойство класса Element, значение которого необходимо отображать в списке.
- comboBox1.SelectedIndex = 0;
- }
- public Element SelectedItem
- {
- get
- {
- return comboBox1.SelectedItem as Element;
- }
- }
Изменение типоразмера вложенного семейства
После того как мы показали форму выбора пользователю и пользователь выбрал требуемый тип панели, применение выбранного типа к шкафам достигается очень просто. Нужно всего лишь задать значение параметра Тип панели двери для всех выбранных шкафов. Очевидно, что изменение параметра необходимо делать в рамках транзакции.
- // Отображение формы для выбора типа панели дверей
- DoorPanelTypeSelectorForm form
- = new DoorPanelTypeSelectorForm(
- current_door_panel_type_name,
- door_panel_types );
- if( System.Windows.Forms.DialogResult.OK
- == form.ShowDialog() )
- {
- FamilySymbol door_panel_type
- = form.SelectedItem as FamilySymbol;
- ElementId id = door_panel_type.Id;
- using( Transaction tx = new Transaction( doc ) )
- {
- tx.Start( "Изменение типа панели" );
- foreach( Element e in casework )
- {
- Parameter p = e.get_Parameter(
- "Door Panel Type" ); // Тип панели двери
- p.Set( id );
- }
- tx.Commit();
- }
- }
Внешнее приложение
Реализация класса реализующего интерфейс IExternalApplication очень проста.
Так как нам в конечном итоге нужно отобразить три команды, мы можем использовать StackedPanel для отображения трех команд на ленте.
Вот полный код класса:
- class App : IExternalApplication
- {
- /// <summary>
- /// Добавление трех команд
- /// на панель ленты
- /// </summary>
- void PopulatePanel( RibbonPanel p )
- {
- string path = Assembly.GetExecutingAssembly()
- .Location;
- RibbonItemData i1 = new PushButtonData(
- "TableLoadPlace", "1 Загрузка и размещение стола",
- path, "FamilyApi.CmdTableLoadPlace" );
- i1.ToolTip = "Загрузка семейства Стол и размещение экземпляра семейства в проекте";
- RibbonItemData i2 = new PushButtonData(
- "TableModify", "2 Новый типоразмер стола",
- path, "FamilyApi.CmdTableNewTypeModify" );
- i2.ToolTip = "Создание нового типоразмера стола и применение нового типоразмера к экземпляру";
- RibbonItemData i3 = new PushButtonData(
- "KitchenUpdate", "3 Обновление кухни",
- path, "FamilyApi.CmdKitchenUpdate" );
- i3.ToolTip = "Выбор и изменение всех кухонных шкафов.";
- p.AddStackedItems( i1, i2, i3 );
- }
- public Result OnStartup(
- UIControlledApplication a )
- {
- PopulatePanel(
- a.CreateRibbonPanel(
- Util.Caption ) );
- return Result.Succeeded;
- }
- public Result OnShutdown(
- UIControlledApplication a )
- {
- return Result.Succeeded;
- }
- }
Заключение
Я думаю в наконец то закрыл все вопросы связанные с использованием API для работы с семействами.
Я очень счастлив, что у меня получилась довольно простая реализация всех команд.
Ну и наконец, я хотел бы выложить архив с полным исходным кодом для Visual Studio всех команд, которые мы обсуждали в нашей серии статей, а также семейство Стол и модель кухни.
Обсуждение: http://adn-cis.org/forum/index.php?topic=426
Опубликовано 30.12.2013Отредактировано 30.12.2013 в 09:22:28