Skip to content
C#
API 概览与核心概念

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();

⚡ 链式调用中,尽量使用 OfClassOfCategory 等"快速过滤"方法。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;
    }
}