API 概览与核心概念
本章讲解 Revit API 的核心架构:Application、Document、Element、Parameter,以及过滤器系统。这些是 Revit 二次开发的骨架。
核心对象层次
Application (全局单例)
├── UIApplication
│ └── UIDocument(活动文档的 UI 视图)
│ └── Document (BIM 数据库)
│ ├── Element (所有 BIM 对象的基类)
│ │ ├── Wall, Floor, Roof, Door...
│ │ ├── FamilyInstance
│ │ ├── FamilySymbol (类型)
│ │ └── View, Level, Grid...
│ ├── Parameter (元素的属性)
│ └── Transaction (修改数据库的单元)
└── UIControlledApplication
├── CreateRibbonTab / CreateRibbonPanel
└── ControlledApplication (事件注册)Application vs UIApplication
// IExternalApplication.OnStartup 中
public Result OnStartup(UIControlledApplication uiApp)
{
// UIControlledApplication:创建 Ribbon、注册事件
uiApp.CreateRibbonTab("MyTab");
Autodesk.Revit.ApplicationServices.Application app =
uiApp.ControlledApplication;
// ControlledApplication:应用级事件
app.DocumentOpened += OnDocOpened;
app.ApplicationInitialized += OnAppInitialized;
return Result.Succeeded;
}
// IExternalCommand.Execute 中
public Result Execute(ExternalCommandData commandData, ...)
{
UIApplication uiApp = commandData.Application;
UIDocument uiDoc = uiApp.ActiveUIDocument;
Document doc = uiDoc.Document;
}对象层次对比
| 对象 | 来源 | 用途 |
|---|---|---|
UIControlledApplication |
OnStartup 参数 |
初始化 Ribbon、注册事件 |
ControlledApplication |
UIControlledApplication.ControlledApplication |
应用级事件 |
UIApplication |
commandData.Application |
获取 UIDocument、打开文件 |
UIDocument |
uiApp.ActiveUIDocument |
获取选择集、显示元素 |
Document |
uiDoc.Document |
所有数据库操作 |
Application |
commandData.Application.Application |
应用级属性 |
Document —— 项目数据库
Document doc = commandData.Application.ActiveUIDocument.Document;
// 文档信息
string title = doc.Title;
string path = doc.PathName;
bool isFamilyDoc = doc.IsFamilyDocument;
bool isModifiable = doc.IsModifiable; // 是否可编辑
// 项目信息
ProjectInfo projInfo = doc.ProjectInformation;
string projNumber = projInfo.Number;
string projName = projInfo.Name;
string clientName = projInfo.ClientName;
// 单位
FormatOptions fo = doc.GetUnits().GetFormatUnits(UnitType.UT_Length);
double conversion = fo.GetUnitTypeId().GetConversionFactor(doc); // 英尺→毫米🔬 Revit 内部使用英制(英尺)存储所有长度值。你需要根据项目单位转换显示值。幸运的是,Revit 2018+ 的
UnitUtils简化了转换:
// Revit 内部 → 毫米
double mm = UnitUtils.ConvertFromInternalUnits(lengthInFeet, UnitTypeId.Millimeters);
// 毫米 → Revit 内部
double feet = UnitUtils.ConvertToInternalUnits(mm, UnitTypeId.Millimeters);Element —— 万物皆元素
Revit 中几乎所有对象都继承自 Element:
Element
├── Wall
├── Floor
├── RoofBase
├── FamilyInstance (门、窗、家具等)
├── FamilySymbol (族类型)
├── View (平面图、立面图、三维视图)
├── Level (标高)
├── Grid (轴网)
├── Room
├── Area
├── TextNote
├── Dimension
├── ModelLine
└── ...元素基本信息
Element element = doc.GetElement(elementId);
// 基本属性
ElementId id = element.Id;
string name = element.Name;
string category = element.Category?.Name; // 如 "墙"、"门"
ElementId categoryId = element.Category?.Id;
BuiltInCategory bic = (BuiltInCategory)categoryId.IntegerValue;
// 元素类型 vs 实例
// FamilySymbol = 类型(如"基本墙:常规-200mm")
// FamilyInstance = 实例(放置在模型中的那堵具体的墙)Category(类别)
Category 是 Revit 分类系统的核心:
// 获取文档中所有类别
Categories categories = doc.Settings.Categories;
// 获取特定类别
Category wallsCategory = doc.Settings.Categories.get_Item(BuiltInCategory.OST_Walls);
Category doorsCategory = doc.Settings.Categories.get_Item(BuiltInCategory.OST_Doors);
// 遍历类别
foreach (Category cat in categories)
{
if (cat.CategoryType == CategoryType.Model) // 模型类别
{
Debug.Print($"{cat.Name} - {cat.Id.IntegerValue}");
}
}常用 BuiltInCategory
| BuiltInCategory | 含义 |
|---|---|
OST_Walls |
墙 |
OST_Floors |
楼板 |
OST_Roofs |
屋顶 |
OST_Doors |
门 |
OST_Windows |
窗 |
OST_StructuralColumns |
结构柱 |
OST_StructuralFraming |
结构框架 |
OST_Levels |
标高 |
OST_Grids |
轴网 |
OST_Rooms |
房间 |
OST_TextNotes |
文字注释 |
OST_GenericModel |
通用模型 |
FilteredElementCollector —— 元素收集器
这是 Revit API 中最常用的类,用于从文档中检索元素:
// 获取文档中所有墙
var walls = new FilteredElementCollector(doc)
.OfClass(typeof(Wall))
.WhereElementIsNotElementType() // 只取实例,排除类型
.Cast<Wall>()
.ToList();
// 获取所有族类型
var symbols = new FilteredElementCollector(doc)
.OfClass(typeof(FamilySymbol))
.Cast<FamilySymbol>()
.ToList();
// 多重过滤
var doorsOnLevel = new FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_Doors)
.WhereElementIsNotElementType()
.Cast<FamilyInstance>()
.Where(door => door.LevelId == targetLevelId)
.ToList();常用过滤方法链
| 方法 | 用途 |
|---|---|
OfClass(typeof(T)) |
按 .NET 类型过滤 |
OfCategory(BuiltInCategory) |
按 Revit 类别过滤 |
OfCategoryId(ElementId) |
按 Category Id 过滤 |
WhereElementIsElementType() |
只取类型(FamilySymbol 等) |
WhereElementIsNotElementType() |
只取实例 |
WherePasses(ElementFilter) |
自定义过滤器 |
Where(predicate) |
LINQ 条件(慎用,遍历所有) |
快速过滤 vs 慢速过滤
// ✅ 快速过滤 —— 在 Revit 数据库层执行(O(log n) 或 O(1))
var walls = new FilteredElementCollector(doc)
.OfClass(typeof(Wall)) // 快速
.OfCategory(BuiltInCategory.OST_Walls) // 快速
.WhereElementIsNotElementType() // 快速
.ToElements();
// ❌ 慢速过滤 —— 先加载全部元素再在内存中过滤(O(n))
var walls = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.Where(e => e.Name.Contains("墙")) // ⚠ 慢速!无索引搜索
.ToElements();⚡ 链式调用中,尽量使用
OfClass、OfCategory等"快速过滤"方法。Where()是 LINQ 扩展,会先提取所有元素再遍历——在大型项目中极慢。
元素 vs 元素类型
Revit 有一个关键的设计概念:类型与实例分离。
// 实例(Instance)—— 项目中具体的墙
Wall wall = wallElement as Wall;
ElementId typeId = wall.GetTypeId(); // 获取类型 ID
WallType wallType = doc.GetElement(typeId) as WallType; // 获取类型对象
// 类型(Type / Symbol)—— 定义共享属性的模板
string familyName = wallType.FamilyName; // "基本墙"
string typeName = wallType.Name; // "常规-200mm"
CompoundStructure structure = wallType.GetCompoundStructure(); // 复合结构层
// Family vs FamilySymbol vs FamilyInstance
// Family = 族定义(如"矩形柱")
// FamilySymbol = 族类型(如"450×450mm")
// FamilyInstance = 族实例(项目中那个具体的柱子)完整工作流:统计墙类型数量
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class CountWallsCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData,
ref string message, ElementSet elements)
{
Document doc = commandData.Application.ActiveUIDocument.Document;
// 收集所有墙实例
var walls = new FilteredElementCollector(doc)
.OfClass(typeof(Wall))
.WhereElementIsNotElementType()
.Cast<Wall>()
.ToList();
// 按类型分组统计
var stats = walls
.GroupBy(w => doc.GetElement(w.GetTypeId())?.Name ?? "未知")
.OrderByDescending(g => g.Count());
string report = "墙体统计:\n";
foreach (var group in stats)
{
double totalLength = group.Sum(w =>
UnitUtils.ConvertFromInternalUnits(
w.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH)?.AsDouble() ?? 0,
UnitTypeId.Millimeters
));
report += $" {group.Key}:{group.Count()} 个,总长 {totalLength / 1000:F2} m\n";
}
TaskDialog.Show("墙体统计", report);
return Result.Succeeded;
}
}