Tekla Structures ModelObject API 完全解析
ModelObject 是 Tekla Structures Open API 中最核心的抽象基类,所有模型对象(Beam、BoltArray、Weld、Assembly 等)均直接或间接继承自此类。它定义了模型对象的通用行为:CRUD 操作、用户属性读写、报告属性访问、坐标系统获取、阶段管理等。
本文基于 Tekla.Structures.Model 程序集(Version=2017.0.0.0)的反编译源码,深入解析 ModelObject 的完整继承体系、构造机制、属性方法及最佳实践。
一、继承体系
ModelObject 位于 Tekla 模型对象层次结构的中心位置,声明为 abstract,不能直接实例化:
└── ModelObject (abstract)
├── Part (abstract) → Beam, ContourPlate, Brep, BentPlate...
├── BaseComponent (abstract) → Connection, Component, CustomPart...
├── Assembly
└── Weld, BoltArray, RebarGroup, Load... 等共 61 种类型
ModelObject 实现了 IComparable 和 IEquatable 接口,支持排序和相等比较;标记 [Serializable] 和 [ClassInterface(ClassInterfaceType.AutoDual)],支持 COM 互操作和序列化。内部枚举 ModelObjectEnum 定义了全部 61 种模型对象类型标识符。
•
ModelObject 声明为 abstract,不能直接实例化• 实现
IComparable 和 IEquatable 接口• 标记
[Serializable] 和 [ClassInterface],支持 COM 互操作•
ModelObjectEnum 定义了全部 61 种模型对象类型标识符| 层级 | 类型 | 说明 |
|---|---|---|
| Level 0 | System.Object |
.NET 基础对象类 |
| Level 1 | ModelObject(抽象) |
核心抽象基类,定义 CRUD + 属性操作 |
| Level 2-a | Part(抽象) |
构件基类,派生 Beam、ContourPlate 等 |
| Level 2-b | BaseComponent(抽象) |
组件基类,派生 Connection、Component 等 |
| Level 2-c | Assembly |
装配体,包含多个子构件 |
| Level 2-d | Brep |
B-Rep 几何实体 |
| Level 3+ | 具体业务类 | Beam, BoltArray, Weld, RebarGroup 等 61 种 |
二、构造函数
ModelObject 作为抽象基类,其自身没有公开的构造函数。子类的构造遵循以下模式:
| 步骤 | 说明 |
|---|---|
| ① 调用子类构造 | 通过具体子类创建实例:new Beam()、new BoltArray() 等 |
| ② 基类链初始化 | Object → ModelObject 构造链依次执行 |
| ③ 字段默认值 | modificationTimeInfo = null(延迟加载),ObjectLabel = null |
| ④ 标识符状态 | 新创建的对象 Identifier.ID = 0(无效),调用 Select() 或 Insert() 后获得有效 ID |
Insert() 方法才能将对象持久化到模型中。三、属性
3.1 自身属性
| 属性名 | 类型 | 可读写 | 说明 |
|---|---|---|---|
ModificationTime |
DateTime? |
只读 | 对象最后修改时间,从数据库 epoch 时间戳转换。首次访问时延迟加载 |
IsUpToDate |
bool |
只读 | 指示对象是否为最新状态。true = 与模型数据库同步;false = 本地有未提交修改 |
ObjectLabel |
string |
内部 | 对象标签(内部使用),通过 SetLabel(string) 设置 |
3.2 ModificationTime 延迟加载机制
ModificationTime 属性采用延迟加载模式。首次访问时,若内部缓存 modificationTimeInfo 为 null,则调用底层 API ExportGetObjectLastModified() 获取时间戳,通过 ConvertDateTime.FromEpoch() 转换为本地时间后缓存。后续访问直接返回缓存值,避免重复 API 调用。如果 API 调用失败,则返回 null 并设置 IsLocallyModified = true。
3.3 继承属性(源自 Part / ModelObject 子类)
以下为最常用的继承属性,具体子类(Beam、ContourPlate 等)会在此基础上扩展更多专用属性:
| 属性 | 类型 | 继承自 | 说明 |
|---|---|---|---|
Identifier |
Identifier |
ModelObject |
模型对象的唯一标识符 |
Name |
string |
Part |
零件名称 |
Class |
string |
Part |
零件等级 |
Finish |
string |
Part |
表面处理代码(如 GALV) |
PartNumber |
string |
Part |
零件编号 |
AssemblyNumber |
string |
Part |
构件编号 |
Profile |
Profile |
Part |
实体的截面 |
Material |
Material |
Part |
实体的材质 |
Position |
Position |
Part |
控制实体在截面方向上的位置 |
UserPhase |
int |
ModelObject |
用户定义的状态阶段 |
四、枚举类型
ModelObject 定义了一个重要的内部公开枚举 ModelObjectEnum,用于标识 Tekla 模型中的所有对象类型:
| 枚举值 | 数值 | 对应类 |
|---|---|---|
BEAM |
1 | 梁/柱(Beam) |
POLYBEAM |
2 | 折线梁(PolyBeam) |
CONTOURPLATE |
3 | 等高板(ContourPlate) |
BOOLEANPART |
4 | 布尔切割件(BooleanPart) |
FITTING |
5 | 适配件(Fitting) |
CUTPLANE |
6 | 切割面(CutPlane) |
WELD |
8 | 焊缝(Weld) |
ASSEMBLY |
9 | 装配体(Assembly) |
SINGLEREBAR |
10 | 单根钢筋(SingleRebar) |
REBARGROUP |
11 | 钢筋组(RebarGroup) |
BOLT_ARRAY |
15 | 螺栓阵列(BoltArray) |
CONNECTION |
24 | 节点(Connection) |
COMPONENT |
25 | 组件(Component) |
CUSTOM_PART |
43 | 自定义零件(CustomPart) |
BREP |
52 | B-Rep 实体(Brep) |
BENT_PLATE |
61 | 折弯板(BentPlate) |
(共 61 种类型,上表仅列出最常用的 16 种。完整列表含 REBARMESH、LOAD_*、GRID、TASK 等。)
PropertySourceEnum(SOURCE_UDA / SOURCE_REPORT / SOURCE_ALL_UDAS),用于区分属性来源,仅内部使用。五、方法
5.1 抽象方法(必须由子类实现)
| 方法 | 返回类型 | 说明 |
|---|---|---|
Insert() |
bool |
将当前对象插入模型数据库。成功返回 true |
Select() |
bool |
根据 Identifier 从数据库选择对象并填充属性 |
Modify() |
bool |
将本地修改提交到模型数据库 |
Delete() |
bool |
从模型数据库删除当前对象 |
5.2 实例方法
| 方法 | 返回类型 | 说明 |
|---|---|---|
GetChildren() |
ModelObjectEnumerator |
获取当前对象的所有子对象。需 Identifier 有效 |
GetFatherComponent() |
BaseComponent |
获取父组件(Connection/Component/Detail/CustomPart)。无父组件时返回 null |
GetHierarchicObjects() |
ModelObjectEnumerator |
获取关联的层级对象 |
GetAllUserProperties(ref Hashtable) |
bool |
获取全部用户自定义属性(UDA),以键值对形式存入 Hashtable |
GetStringUserProperties(ref Hashtable) |
bool |
获取字符串类型的 UDA |
GetDoubleUserProperties(ref Hashtable) |
bool |
获取双精度浮点型的 UDA |
GetIntegerUserProperties(ref Hashtable) |
bool |
获取整数类型的 UDA |
GetUserProperty(string, ref string) |
bool |
按名称获取单个字符串 UDA(3 个重载:string/double/int) |
SetUserProperty(string, string) |
bool |
按名称设置单个 UDA(3 个重载:string/double/int) |
GetAllReportProperties(...) |
bool |
批量获取报告属性(支持 String/Double/Integer 三种类型) |
GetReportProperty(string, ref T) |
bool |
按名称获取单个报告属性(3 个重载) |
GetDynamicStringProperty(string, ref string) |
bool |
从数据库获取动态字符串属性(长文本自动分段拼接) |
SetDynamicStringProperty(string, string) |
bool |
设置动态字符串属性(超过512字符自动分段写入) |
GetCoordinateSystem() |
CoordinateSystem |
获取对象的局部坐标系统。返回 null 表示无效 |
SetPhase(Phase) |
bool |
设置对象的施工阶段。需 Identifier 有效 |
GetPhase(out Phase) |
bool |
获取对象的施工阶段信息 |
SetLabel(string) |
int |
设置对象标签。传入 null 抛出 ArgumentNullException |
CompareTo(object) |
int |
实现 IComparable,按 Identifier.ID 比较 |
Equals(ModelObject) |
bool |
实现 IEquatable,按 ID 判断同一模型实体 |
5.3 内部 / 受保护方法
| 方法 | 访问级别 | 说明 |
|---|---|---|
DeleteInstance() |
protected | 执行实际删除操作,通过 ExportDeleteObject 调用底层 API |
ToStruct(ref dotModelObject_t) |
internal | 将 ModelObject 序列化为互操作结构体 |
FromStruct(ref dotModelObject_t) |
internal | 从互操作结构体反序列化回 ModelObject |
InitializeProperties(...) |
private static | 批量初始化属性查询请求(每次最多 10 个属性) |
IntGetProperties(...) |
private | 内部属性查询入口,封装 ExportGetProperties 调用 |
IntSetUserProperty(...) |
private | 内部属性设置入口,封装 ExportSetProperty 调用 |
六、完整代码示例
示例 1:创建 Beam 对象并插入模型
usingSystem;usingTekla.Structures.Model;usingTekla.Structures.Geometry3d;publicvoidCreateBeamExample(){// 创建一个新的 Beam 对象(继承自 ModelObject)BeammyBeam=newBeam();// 此时 myBeam.Identifier.ID == 0(无效),仅存在于内存Console.WriteLine($"Before Insert - ID: {myBeam.Identifier.ID}");// 输出: 0// 设置必要属性myBeam.StartPoint=newPoint(0,0,0);myBeam.EndPoint=newPoint(0,0,5000);myBeam.Profile.ProfileString="HEA400";myBeam.Material.MaterialString="S235JR";myBeam.Class="2";// 构件等级// 调用 Insert() 将对象写入模型数据库boolinsertSuccess=myBeam.Insert();if(insertSuccess){Console.WriteLine($"Beam inserted! ID: {myBeam.Identifier.ID}");Console.WriteLine($"Label: {myBeam.Identifier}");// 插入后 Identifier.ID 变为有效值(非零正整数)}else{Console.WriteLine("Failed to insert beam. Check geometry and properties.");// 常见失败原因:起点终点重合、型材不存在、材料无效等}}
示例 2:查询对象属性与修改
publicvoidQueryAndModifyBeam(){// 通过 Identifier 选择已有对象BeambeamToQuery=newBeam();beamToQuery.Identifier=newIdentifier(123456);// 已知的目标 IDif(beamToQuery.Select())// 从数据库加载数据{// ===== 读取基本属性 =====Console.WriteLine($"Start: {beamToQuery.StartPoint}");Console.WriteLine($"End: {beamToQuery.EndPoint}");Console.WriteLine($"Profile: {beamToQuery.Profile.ProfileString}");// ===== 读取用户自定义属性 (UDA) =====stringcommentValue="";if(beamToQuery.GetUserProperty("COMMENT",refcommentValue)){Console.WriteLine($"Comment UDA: {commentValue}");}doubleweight=0;if(beamToQuery.GetUserProperty("WEIGHT",refweight)){Console.WriteLine($"Weight UDA: {weight} kg");}// ===== 读取报告属性 =====stringreportName="";beamToQuery.GetReportProperty("NAME",refreportName);Console.WriteLine($"Report NAME: {reportName}");// ===== 读取修改时间和同步状态 =====Console.WriteLine($"Modification Time: {beamToQuery.ModificationTime}");Console.WriteLine($"Is Up To Date: {beamToQuery.IsUpToDate}");// ===== 读取施工阶段 =====PhasecurrentPhase;if(beamToQuery.GetPhase(outcurrentPhase)){Console.WriteLine($"Phase: {currentPhase.PhaseNumber} - {currentPhase.PhaseName}");}// ===== 修改属性 =====beamToQuery.Class="3";// 更改构件等级// 修改 UDAbeamToQuery.SetUserProperty("COMMENT","Modified by API");beamToQuery.SetUserProperty("WEIGHT",1234.5);// 提交修改到数据库boolmodifyOk=beamToQuery.Modify();Console.WriteLine($"Modify result: {modifyOk}");}else{Console.WriteLine("Object not found or selection failed.");}}
示例 3:删除对象
publicboolDeleteModelObjectByIdentifier(longobjectId){try{// 使用 Beam 作为示例,任何 ModelObject 子类均可BeamtargetBeam=newBeam();targetBeam.Identifier=newIdentifier(objectId);// 先 Select 验证对象是否存在if(!targetBeam.Select()){Console.WriteLine($"Object with ID={objectId} does not exist.");returnfalse;}// 执行删除操作booldeleteResult=targetBeam.Delete();if(deleteResult){Console.WriteLine($"Successfully deleted object ID={objectId}");// 删除后 Identifier.ID 被置为 0Console.WriteLine($"ID after delete: {targetBeam.Identifier.ID}");}else{Console.WriteLine($"Delete failed for object ID={objectId}");// 可能原因:权限不足、对象被锁定、被其他对象引用等}returndeleteResult;}catch(Exceptionex){Console.WriteLine($"Error during delete: {ex.Message}");returnfalse;}}// 批量删除示例:删除特定装配体内的所有构件publicvoidDeleteAllComponentsInAssembly(longassemblyId){Assemblyasm=newAssembly();asm.Identifier=newIdentifier(assemblyId);if(asm.Select()){ModelObjectEnumeratorchildren=asm.GetChildren();intdeletedCount=0;foreach(ModelObjectchildinchildren){if(child.Delete())deletedCount++;}Console.WriteLine($"Deleted {deletedCount} child objects from assembly.");}}
示例 4:遍历模型对象
usingSystem.Collections;publicvoidEnumerateAllBeams(){Modelmodel=newModel();// ===== 方式一:获取所有 Beam 类型对象 =====ModelObjectEnumeratorbeamEnumerator=model.GetModelObjectSelector().GetAllObjectsWithType(ModelObject.ModelObjectEnum.BEAM);ArrayListbeamList=newArrayList();while(beamEnumerator.MoveNext()){BeamcurrentBeam=beamEnumerator.CurrentasBeam;if(currentBeam!=null&¤tBeam.Select()){beamList.Add(currentBeam);// 读取每根梁的信息stringprofile="";if(currentBeam.GetUserProperty("PROFILE",refprofile)){Console.WriteLine($"[{currentBeam.Identifier.ID}] "+$"({currentBeam.StartPoint.X:F0}, {currentBeam.StartPoint.Y:F0}, {currentBeam.StartPoint.Z:F0}) "+$"-> Profile: {profile}");}}}Console.WriteLine($"Total beams found: {beamList.Count}");// ===== 方式二:遍历装配体及其子对象 =====ModelObjectEnumeratorasmEnumerator=model.GetModelObjectSelector().GetAllObjectsWithType(ModelObject.ModelObjectEnum.ASSEMBLY);while(asmEnumerator.MoveNext()){Assemblyasm=asmEnumerator.CurrentasAssembly;if(asm!=null&&asm.Select()){Console.WriteLine($"\nAssembly ID: {asm.Identifier.ID}");// 获取装配体的所有子对象ModelObjectEnumeratorchildren=asm.GetChildren();while(children.MoveNext()){ModelObjectchild=children.Current;// 获取子对象的父组件信息BaseComponentfather=child.GetFatherComponent();stringfatherInfo=father!=null?father.Identifier.ToString():"(none)";Console.WriteLine($" Child [{child.GetType().Name}] "+$"ID={child.Identifier.ID}, FatherComp={fatherInfo}");// 读取子对象的坐标系统CoordinateSystemcoordSys=child.GetCoordinateSystem();if(coordSys!=null){Console.WriteLine($" Origin: {coordSys.Origin}");}}}}// ===== 方式三:按条件筛选 + 排序 =====ModelObjectEnumeratorallObjects=model.GetModelObjectSelector().GetAllObjects();ArrayListsortedList=newArrayList();while(allObjects.MoveNext()){ModelObjectobj=allObjects.Current;sortedList.Add(obj);}// 利用 IComparable 接口进行排序sortedList.Sort();// 按 Identifier.ID 升序排列Console.WriteLine("\n--- Sorted objects by ID ---");foreach(ModelObjectobjinsortedList){Console.WriteLine($" {obj.GetType().Name,-20} ID={obj.Identifier.ID}");}}
七、常见问题与注意事项
- Identifier 必须有效:几乎所有需要与数据库交互的方法都要求
Identifier.IsValid() == true。新对象在Insert()之前 ID 为 0(无效)。 - Select 后才能操作属性:通过
new Beam()创建的空对象不包含数据库中的真实属性值。必须先调用Select()才能正确读取和修改属性。 - Insert 前必须设置必要属性:不同子类有不同的前提要求。例如
Beam需要 StartPoint、EndPoint、Profile、Material 等有效值。 - ModificationTime 延迟加载:首次访问时会调用底层 API 获取时间戳。频繁遍历大量对象时可能产生性能开销。标记
[NonSerialized]。 - UserProperty vs ReportProperty:
GetUserProperty读取用户自定义属性(UDA),GetReportProperty读取模板计算出的报告属性。两者是不同的数据源。 - 动态字符串属性的 512 字符限制:
SetDynamicStringProperty和GetDynamicStringProperty在底层以 512 字符为单位分段传输,API 会自动处理。 - Delete 后 ID 归零:删除成功后执行
Identifier.ID = 0,该对象引用已失效,不能再用于其他操作。 - Equals 基于 Identifier 比较:两者都有有效 Identifier 时仅比较 ID;若任一方无效,则退回到
object.Equals()引用比较。 - CompareTo 用于集合排序:实现了
IComparable接口,可直接对ArrayList调用Sort()按 ID 升序排列。
1. 批量操作优于单条操作 — 使用
GetAllUserProperties 一次性获取所有 UDA2. 减少 Select 调用 — 通过模型选择器遍历得到的对象已经过 Select
3. 避免不必要的 ModificationTime 读取 — 大批量遍历时跳过此属性
4. 事务性操作 — 对于大批量插入/修改,使用
Model.CommitChanges() 进行事务管理八、总结
ModelObject是 Tekla Open API 中最核心的抽象基类,所有模型对象均继承自此类。- 声明为
abstract不可直接实例化,需通过具体子类(Beam、ContourPlate等)创建对象。 - 提供了 4 个抽象 CRUD 方法(
Insert、Select、Modify、Delete),子类必须实现。 - 用户属性(UDA)与报告属性(Report)操作均提供 string/double/int 三种类型的读写重载。
GetDynamicStringProperty/SetDynamicStringProperty支持超过 512 字符的长文本,自动分段传输。- 通过
GetCoordinateSystem()获取局部坐标系统,SetPhase()/GetPhase()管理施工阶段。 - 实现了
IComparable和IEquatable,支持按 ID 排序和相等比较。 ModificationTime采用延迟加载,IsUpToDate指示同步状态。- 内部通过
DelegateProxy层与 Tekla C++ 内核通信,所有属性操作经 P/Invoke 调用。
本文适用于 Tekla Structures 2017 及以上版本。
Tekla 二次开发:ModelObject API 完全解析 - 钢结构资源网 Tekla插件 CAD工具 犀牛GH汉化 套料



Tekla 二次开发:Brep API 完全解析
探索者TSSD2024和谐版
