Скрыть все элементы кроме элементов заданной системы
На форуме, у пользователя goblya, возник интересный вопрос. Так как решение его проблемы оказалось не таким-то уж и простым, и пришлось повозиться с окончательным решением, то решил создать статью с решением.
Итак, задача. Необходимо скрыть все элементы на виде, кроме элементов системы, к которой принадлежит выбранный элемент.
У пользователя это почти получилось. Действовал он в правильном направлении, однако, некоторые элементы все же не скрывались.
- Parameter Namesystem = elem.get_Parameter(BuiltInParameter.RBS_SYSTEM_NAME_PARAM);
- View view = doc.ActiveView;
- IList<ElementId> categories = new List<ElementId>();
- categories.Add(new ElementId(BuiltInCategory.OST_PlaceHolderDucts));
- categories.Add(new ElementId(BuiltInCategory.OST_DuctLinings));
- categories.Add(new ElementId(BuiltInCategory.OST_DuctInsulations));
- categories.Add(new ElementId(BuiltInCategory.OST_DuctTerminal));
- categories.Add(new ElementId(BuiltInCategory.OST_MechanicalEquipment));
- IList<FilterRule> rules = new List<FilterRule>();
- rules.Add(ParameterFilterRuleFactory.CreateNotContainsRule(Namesystem.Id, Namesystem.AsString(), true));
- ParameterFilterElement filter = null;
- using (Transaction t = new Transaction(doc, "Create and Apply Filter"))
- {
- t.Start();
- filter = ParameterFilterElement.Create(doc, "2222", categories, rules);
- view.AddFilter(filter.Id);
- t.Commit();
- }
- using (Transaction t = new Transaction(doc, "Set Visibility Appearance"))
- {
- t.Start();
- view.SetFilterVisibility(filter.Id, false);
- t.Commit();
- }
у меня не все элементы исчезают ,которые принадлежат другим системам. я так понимаю нужно добавить строки categories.Add(new ElementId(BuiltInCategory.OST_PlaceHolderDucts)); все что начинается на OST_Duct ?
То есть решение задачи сводится к созданию правильного фильтра и применения этого вида к фильтру.
В фильтре необходимо задавать категории, к которым применяется фильтр. Конечно, в теории можно было бы выбрать все категории, название которых начинается с OST_Duct, но в таком случае, решение задачи было бы частным и работало бы только с Механизмами.
Первоначально, я понял задачу, что необходимо скрыть абсолютно все элементы, кроме элементов системы. В этом случае, категория нас совсем не интересует.
Но, в метод ParameterFilterElement.Create в любом случае надо передать список категорий. В этом случае, условие фильтра получается таким: Выбрать все элементы всех категорий, у которых параметр ‘Имя системы’ не равен названию системы выбранного элемента.
Таким образом, нужно передать все категории в метод:
- var allCategories =
- doc.Settings.Categories.OfType<Category>().Select(c => c.Id).ToList();
- var filter = ParameterFilterElement
- .Create(doc,
- string.Format("Все элементы, кроме системы {0}", system.Name),
- allCategories,
- rules);
Первая неудача. Получим исключение 'One of the given categories is not filterable' (Одна из заданных категорий не может быть использована для фильтрации).
Что ж. Значит не все категории можно включить в фильтр. Значит, надо выбрать только те категории, который могут быть использованы для фильтрации.
Немного покопавшись в файле справки к Revit API находим полезный класс ParameterFilterUtilities и не менее полезный метод ParameterFilterUtilities.GetAllFilterableCategories(). Метод возвращает список категорий, которые можно использовать для фильтрации. Причем возвращаемое значение метода – ICollection<ElementId> - точно такое же, как и необходимо использовать в метода ParameterFilterElement.Create.
Попытка номер 2.
- var filter = ParameterFilterElement
- .Create(doc,
- string.Format("Все элементы, кроме системы {0}", system.Name),
- ParameterFilterUtilities.GetAllFilterableCategories(),
- rules);
Снова неудача. One of the given rules refers to a parameter that doesn’t apply to this filter’s categories. (Один из фильтров параметра не может быть применен к заданному списку категорий)
Если используется условия фильтра по параметру, то этот параметр должен содержаться в заданных категориях. Очевидно, что параметр Имя системы используется не для всех категорий.
Таким образом, необходимо создать уже два фильтра.
- Выбрать все элементы, категории которых НЕ имеют параметра Имя системы
- Выбрать все элементы, которые имею параметр Имя системы, но название системы не соответствует заданному.
Для создания таких фильтров, нужно выбрать категории, которые содержат параметр Имя системы, и, которые не содержат.
Снова смотрим в методы класса ParameterFilterUtilities. Конкретно того, что нам нужно, нет. Нам нужно на вход параметр, а на выходе получить список категорий. Но, тем не менее, есть подходящие методы, из которых мы можем извлечь то что нужно. ParameterFilterUtilities.GetFilterableParametersInCommon – возвращает список параметров, который доступны для заданного списка категорий.
То есть, если для каждой категории можно узнать, можно ли использовать определенный для фильтрации параметр или нет.
В результате родился вот такой метод:
- /// <summary>
- /// Возвращает список категорий, которые можно использовать для фильтрации
- /// по заданному параметру
- /// </summary>
- /// <param name="doc">Доеумент, в котором необходимо выполнить фильтрацию</param>
- /// <param name="bip">Встроенный параметр</param>
- /// <param name="inverse">Если true - то список будет инвертирован,
- /// Т.е. получим список категорий, которые нельзя использовать для фильтрации
- /// с данным параметром</param>
- /// <returns></returns>
- ICollection<ElementId> GetCategoriesApplicableForParameter(Document doc,
- BuiltInParameter bip,
- bool inverse = false)
- {
- // Берем все категории, доступные для фильтрации
- var allCategories = ParameterFilterUtilities.GetAllFilterableCategories();
- ICollection<ElementId> retResult = new List<ElementId>();
- // для каждой категории
- foreach (ElementId categoryId in allCategories)
- {
- // получаем список параметров, доступных для фильтрации с этой категорией
- var applicableParameters =
- ParameterFilterUtilities.GetFilterableParametersInCommon(doc, new[] { categoryId });
- // если среди параметров есть интересующий нас параметр
- // добавляем его в коллекцию
- if (applicableParameters.Contains(new ElementId(bip)))
- {
- retResult.Add(categoryId);
- }
- }
- // Инвертируем список, если необходимо.
- if (inverse)
- retResult =
- allCategories.Where(x => !retResult.Contains(x)).ToList();
- return retResult;
- }
Возвращаясь к нашей задаче, теперь, чтобы получить список всех категорий, доступных для фильтрации и позволяющих отфильтровать по имени системы, достаточно написать следующее:
- var categoriesWithSystem =
- GetCategoriesApplicableForParameter(doc, BuiltInParameter.RBS_SYSTEM_NAME_PARAM);
И список категорий, не содержащих параметр Имя системы:
- var categoriesWithoutSystemNameParameter =
- GetCategoriesApplicableForParameter(doc, BuiltInParameter.RBS_SYSTEM_NAME_PARAM, true);
Необходимо помнить, что один элемент может принадлежать нескольким системам. В этом случае, в зависимости от потребностей, нужно отобразить все системы, которым принадлежит элемент, либо оставить только одну.
В случае, если необходимо оставить все системы, то необходимо выделить все системы из параметра и создать несколько условий фильтра:
- IList<FilterRule> rules = new List<FilterRule>();
- var systems = systemName.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
- foreach (var system in systems)
- {
- rules.Add(ParameterFilterRuleFactory
- .CreateNotContainsRule(new ElementId(BuiltInParameter.RBS_SYSTEM_NAME_PARAM),
- system.Trim(), true));
- }
Полный код команды приведен ниже:
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements)
- {
- UIApplication uiapp = commandData.Application;
- UIDocument uidoc = uiapp.ActiveUIDocument;
- Application app = uiapp.Application;
- Document doc = uidoc.Document;
- Reference r;
- try
- {
- r = uidoc.Selection.PickObject(ObjectType.Element,
- new SystemElementFilter(),
- "Выберите элемент системы");
- }
- catch (OperationCanceledException)
- {
- return Result.Cancelled;
- }
- var elem = doc.GetElement(r.ElementId);
- var systemNameParam =
- elem.get_Parameter(BuiltInParameter.RBS_SYSTEM_NAME_PARAM);
- if (systemNameParam == null)
- {
- message = "Вы выбрали не элемент системы";
- return Result.Failed;
- }
- var view = doc.ActiveView;
- var categoriesWithSystem =
- GetCategoriesApplicableForParameter(doc, BuiltInParameter.RBS_SYSTEM_NAME_PARAM);
- var categoriesWithoutSystemNameParameter =
- GetCategoriesApplicableForParameter(doc, BuiltInParameter.RBS_SYSTEM_NAME_PARAM, true);
- var systemName = systemNameParam.AsString();
- // элемент может принадлежать нескольким ситсемам
- // в этом случае в параметре Имя системы они разделены запятыми
- // создаем фильтр по условию И
- IList<FilterRule> rules = new List<FilterRule>();
- var systems = systemName.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
- foreach (var system in systems)
- {
- rules.Add(ParameterFilterRuleFactory
- .CreateNotContainsRule(new ElementId(BuiltInParameter.RBS_SYSTEM_NAME_PARAM),
- system.Trim(), true));
- }
- using (var t = new Transaction(doc, "Изолирование системы"))
- {
- t.Start();
- //Прячем все элементы, которые не содержат параметр Имя системы
- // НЕ ПРИМЕНЯТЬ ФИЛЬТР, ЕСЛИ ТРЕБУЕТСЯ СПРЯТАТЬ ТОЛЬКО СИСТЕМЫ
- ParameterFilterElement filter1 =
- ParameterFilterElement.Create(doc,
- "Все элементы без параметра Имя системы",
- categoriesWithoutSystemNameParameter);
- view.AddFilter(filter1.Id); // применяем фильтр к виду
- view.SetFilterVisibility(filter1.Id, false); // прячем элементы
- // Прячем элементы, которые не принадлежат выбранной системе
- ParameterFilterElement filter2 =
- ParameterFilterElement.Create(doc,
- string.Format("Все элементы не находящиеся в системах {0}", systemName),
- categoriesWithSystem,
- rules);
- view.AddFilter(filter2.Id);
- view.SetFilterVisibility(filter2.Id, false);
- t.Commit();
- }
- return Result.Succeeded;
- }
Класс для реализации выбора элемента системы.
- public class SystemElementFilter : ISelectionFilter
- {
- public bool AllowElement(Element elem)
- {
- var systemNameParam =
- elem.get_Parameter(BuiltInParameter.RBS_SYSTEM_NAME_PARAM);
- return systemNameParam != null && systemNameParam.HasValue;
- }
- public bool AllowReference(Reference reference, XYZ position)
- {
- throw new NotImplementedException();
- }
- }
Как оказалось позднее, нет необходимости скрывать элементы архитектуры, а скрыть нужно только элементы других систем. В этом случае, просто не нужно применять фильтр, который скрывает элементы, у которых нет параметра Имя системы.
В данной реализации также не происходит проверка на существование фильтра. При использовании в реальном проекте, такую проверку конечно же необходимо сделать.
Результат выполнения команды.
До выполнения:
После:
И, в случае с элементом, принадлежащим нескольким системам.
До:
После:
Автор перевода: Виктор Чекалин
Обсуждение: http://adn-cis.org/forum/index.php?topic=1797
Опубликовано 24.01.2015Отредактировано 24.01.2015 в 16:59:48