Bim4Everyone.RevitFiltration — это библиотека C#, разработанная для упрощения создания ElementFilter. Библиотека
состоит из двух основных частей: RevitFiltration и RevitFiltration.Controls.
RevitFiltration.Controls можно использовать для создания ElementFilter, передавая его непосредственно в метод
WherePasses класса FilteredElementCollector.
RevitFiltration.Controls предоставляет пользователю возможность создавать пользовательские фильтры непосредственно в UI,
сохранять их и использовать повторно.
Контрол с возможностью выбрать категории фильтрации и задать правила для параметров:
Контрол с возможностью только задавать правила параметров:
- pyRevitLabs.Json
- Ninject
- dosymep.Revit
- dosymep.Bim4Everyone
- Подключить ссылку на Bim4Everyone.RevitFiltration.dll в .csproj:
<ItemGroup Condition="$(RevitVersion) != ''">
<Reference Include="Bim4Everyone.RevitFiltration">
<HintPath>$(AppData)\pyRevit\Extensions\BIM4Everyone.lib\dosymep_libs\libs\$(RevitVersion)\Bim4Everyone.RevitFiltration.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
- Зарегистрировать в ninject DI контейнере плагина необходимые сервисы:
kernel.UseLogicalFilterFactory(); // сервис для создания фильтра (обязательно)
kernel.UseLogicalFilterParser(); // сервис для сериализации и десериализации фильтра (опционально)
- Задать настройки генерации фильтра через класс
Options.
Из DI контейнера необходимо получить сервис ILogicalFilterFactory, затем сконструировать необходимый фильтр и
сгенерировать ElementFilter, используя класс Options. Чтобы сгенерировать фильтр по типоразмерам (а не по
экземплярам), установите Options.FilterByType = true. Пример:
public void SampleFilterCreation(ILogicalFilterFactory filterFactory, Autodesk.Revit.UI.UIDocument uiDoc) {
ILogicalFilter filter = filterFactory.CreateAndFilter()
.AddEqualsRule(BuiltInParameter.SCHEDULE_LEVEL_PARAM, "Level 1");
var opts = new Options { Tolerance = 1e-6, FilterByType = false };
var elements = new FilteredElementCollector(uiDoc.Document)
.WhereElementIsNotElementType()
.OfCategory(BuiltInCategory.OST_Planting)
.WherePasses(filter.Build(uiDoc.Document, opts))
.ToElementIds();
uiDoc.Selection.SetElementIds(elements);
}
public void SampleFilterParsing(ILogicalFilter filter, ILogicalFilterParser parser) {
string str = parser.Serialize(filter);
bool success = parser.TryParse(str, out ILogicalFilter newFilter);
}
Пространство имён Bim4Everyone.RevitFiltration.Extensions.Materials позволяет отобрать элементы, в которых
используется материал с заданными значениями параметров. Правила задаются тем же ILogicalFilter, но применяются
не к самому элементу, а к материалам документа; значения их параметров сравниваются напрямую, без фильтров Revit.
ILogicalFilter materialsRules = filterFactory.CreateAndFilter()
.AddContainsRule(BuiltInParameter.MATERIAL_NAME, "OSB")
.AddEqualsRule(BuiltInParameter.KEYNOTE_PARAM, "G20");
MaterialsFilter materialsFilter = materialsRules.BuildMaterialsFilter(
doc,
new MaterialOptions { Tolerance = 1e-6, Inverted = false });
var elements = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.WherePasses(paramsFilter)
.PassesFilter(materialsFilter)
.ToArray();
Элемент проходит фильтр, если хотя бы один из отобранных материалов есть в GetMaterialIds(false) либо
GetMaterialIds(true) (материалы краски граней).
То же, что и для Bim4Everyone.RevitFiltration и:
- WPF-UI
- Подключить ссылки на Bim4Everyone.RevitFiltration.dll и Bim4Everyone.RevitFiltration.Controls.dll в .csproj
- Зарегистрировать в ninject DI контейнере плагина необходимые сервисы:
kernel.UseLogicalFilterFactory(); // сервис для создания ILogicalFilter (обязательно)
kernel.UseLogicalFilterProviderFactory(); // сервис для привязки провайдера контекста фильтра из UI к ViewModel (обязательно)
kernel.UseFilterContextParser(); // сервис для сериализации и десериализации контекста фильтра UI (опционально)
- Сконструировать
DataProvider(для значений параметров — по экземплярам элементов либо через собственную функцию).
У класса DataProvider два конструктора:
- значения параметров берутся из экземпляров элементов заданных документов:
DataProvider(categories, getParams, documents); - значения параметров переопределяются собственной функцией (можно вернуть свой список значений):
DataProvider(categories, getParams, getParamValues).
Обоим конструкторам нужны функции получения доступных категорий и параметров. Их можно описать один раз и переиспользовать в любом из примеров ниже:
ICollection<Category> GetCategories(Document doc) {
return ParameterFilterUtilities.GetAllFilterableCategories()
.Select(c => Category.GetCategory(doc, c))
.Where(category => category != null)
.Where(c => c.CategoryType == CategoryType.Model && c.IsVisibleInUI)
.ToArray();
}
ICollection<RevitParam> GetParams(Document doc, ICollection<Category> categories) {
return ParameterFilterUtilities
.GetFilterableParametersInCommon(doc, [..categories.Select(c => c.Id)])
.Select(paramId => GetFilterableParam(doc, paramId))
.Where(p => p != null)
.ToArray();
}
RevitParam GetFilterableParam(Document doc, ElementId paramId) {
try {
if(paramId.IsSystemId()) {
return SystemParamsConfig.Instance.CreateRevitParam(doc, (BuiltInParameter) paramId.GetIdValue());
}
var element = doc.GetElement(paramId);
if(element is SharedParameterElement sharedParameterElement) {
return SharedParamsConfig.Instance.CreateRevitParam(doc, sharedParameterElement.Name);
}
if(element is ParameterElement parameterElement) {
return ProjectParamsConfig.Instance.CreateRevitParam(doc, parameterElement.Name);
}
return null;
} catch(Exception) {
return null;
}
}
Пример 1. Значения параметров берутся из экземпляров элементов заданных документов:
var dataProvider = new DataProvider(
GetCategories(doc),
categories => GetParams(doc, categories),
new[] { doc });
Пример 2. Значения параметров задаются собственной функцией (можно вернуть свой список значений из любого источника — базы данных, конфига, фиксированного списка и т.п.):
ICollection<string> GetCustomValues(ICollection<Category> categories, RevitParam revitParam) {
// здесь — любой свой источник значений; для примера возвращаем фиксированный список
return ["Значение 1", "Значение 2"];
}
var customDataProvider = new DataProvider(
GetCategories(doc),
categories => GetParams(doc, categories),
(categories, revitParam) => GetCustomValues(categories, revitParam));
- Настроить ViewModel окна:
internal class YourViewModel {
public YourViewModel(
ILogicalFilterProviderFactory filterProviderFactory,
ILanguageService languageService,
DataProvider dataProvider) {
FilterProvider = filterProviderFactory.Create(dataProvider);
LanguageService = languageService;
}
public ILogicalFilterProvider FilterProvider { get; } // провайдер для получения фильтра из UI
public ILanguageService LanguageService { get; } // сервис для установки локализации в контроле
}
Чтобы открыть в контроле уже готовый ILogicalFilter (например, полученный из ILogicalFilterParser.TryParse),
используйте перегрузку Create с фильтром и категориями, для которых он был задан:
FilterProvider = filterProviderFactory.Create(
dataProvider,
filter,
new[] { BuiltInCategory.OST_Walls, BuiltInCategory.OST_Floors });
Фильтр загружается только целиком: если хотя бы одна категория недоступна в DataProvider либо хотя бы одно правило
не удается сопоставить с доступными для этих категорий параметрами, провайдер вернет CanGetFilter() == false
и контрол откроется пустым — так же, как при загрузке несовместимого ILogicalFilterContext.
ILogicalFilterProvider уведомляет об изменении контекста фильтра событием FilterContextChanged:
FilterProvider.FilterContextChanged += (sender, args) => {
// args.OldContext, args.NewContext
};
Контрол DynamicCategoriesFilterControl умеет показывать произвольный контент справа снизу, под критериями
фильтрации, в отдельном блоке — например, второй редактор правил для отдельной категории:
<filtration:DynamicCategoriesFilterControl
LanguageService="{Binding LanguageService}"
LogicalFilterProvider="{Binding FilterProvider}">
<filtration:DynamicCategoriesFilterControl.AdditionalContent>
<filtration:PresetCategoriesFilterControl
LanguageService="{Binding LanguageService}"
LogicalFilterProvider="{Binding MaterialsFilterProvider}" />
</filtration:DynamicCategoriesFilterControl.AdditionalContent>
</filtration:DynamicCategoriesFilterControl>
Контенту передаётся DataContext самого контрола, поэтому привязки внутри работают против модели представления
плагина — контент ведёт себя так, будто объявлен там же, где сам контрол. Заданный на контенте DataContext, как
обычно, имеет приоритет. Блок растёт по содержимому, поэтому высоту крупного контента стоит ограничивать через
MaxHeight. Если AdditionalContent не задан, блок не отображается.
- Подключить нужный контрол в xaml:
xmlns:filtration="clr-namespace:Bim4Everyone.RevitFiltration.Controls.Views;assembly=Bim4Everyone.RevitFiltration.Controls"
<filtration:DynamicCategoriesFilterControl
LanguageService="{Binding LanguageService}"
LogicalFilterProvider="{Binding FilterProvider}" />
Компиляция проекта в папку bin
nuke compile
Компиляция проекта в Bim4Everyone.lib\dosymep_libs\libs
nuke publish
Для запуска необходима установка docfx
nuke docs-compile
nuke test

