简介:Tekla Structures作为建筑、桥梁与工业领域广泛使用的三维结构建模软件,其模型、图纸和零件数据往往需要高效管理。围绕该软件的二次开发场景,这份C#示例工程专门解决读取图纸列表并显示零件信息的需求,面向需要批量提取构件属性、生成图纸清单的BIM工程师、结构设计师及Tekla二次开发初学者。工程基于Tekla Open API实现,采用Windows窗体构建,演示了遍历模型Part对象、获取零件ID、名称、材料、尺寸、位置等属性的完整流程;代码中涉及ModelObject.GetAttributes()、Drawing.GetDrawings()等常用API调用,并以工具类形式对部分逻辑做了封装。Tekla模型以对象树方式组织数据,零件是查询的基本单元,图纸清单则需要从文档集合中遍历获取;示例将这些操作集中在可视化窗口中,便于对比界面结果与源码理解API返回值处理。资源共47个文件,压缩包约778KB,主要文件类型包括C#源码、Tekla.Structures系列DLL引用、编译生成的exe与pdb调试文件,以及少量配置和资源文件,目录结构清晰,便于对照窗体设计、启动逻辑、辅助工具和项目设置学习。目前已有2010人学习下载。读者既可以从头学习API调用方式,也可以直接借鉴其窗体与数据分离的组织思路,进一步扩展零件筛选、图纸导出、Excel报表等功能,对完成自动化数据提取与结构分析报告有一定参考价值。
1. 从模型到清单,Tekla二次开发读取零件信息到底解决了什么问题
做过钢结构深化或者混凝土深化的人都有过这种经历:模型快交付时,业主或下游加工厂突然要一份按零件归组的材料清单,于是你打开Tekla模型,一个个选中零件,看属性、抄表格,或者靠着报告模板反复折腾模板字段,一个几百根零件的模型能磨掉半天。某天一个近千根零件的门刚模型摆在我面前,供应商要按零件号、材质、截面、单重分类统计,那一刻我意识到,手工在模型里翻零件信息这件事,翻的不只是零件,是时间和耐心。
Tekla二次开发读取零件信息,说白了就是通过Tekla开放的API,让程序自己进入当前模型,把零件的基础属性、位置信息和工程量数据批量取出来,再按你的规则落到表格或者写回模型里。它能解决的是三类问题:批量整理零件清单、给零件批量打标记或写自定义属性、为后续的工艺和交付流程提供数据源。适合谁用?如果你是Tekla管理员、BIM工程师,或者做模型数据交付的人,下面的思路和代码可以直接拿去做工具。
2. 零件数据藏在哪:搞清Tekla模型里的三层信息结构
2.1 零件信息的三个来源:属性、位置、工程量
Tekla里的一个零件,其实承载了三类数据,读之前必须先分清。第一类是基础属性,包括零件名称、型材截面、材质、等级、抛丸/油漆这些写在对象属性面板里的内容,它们大多挂在零件的标准属性字段上。第二类是位置信息,说的是零件在哪根构件下、属于哪个装配体,这在Tekla的数据结构里是父子关系,零件(Part)挂在装配体(Assembly)下面。第三类是工程量信息,比如净重、毛重、体积、表面积,这些不是手工填的,是Tekla根据几何和材质实时算出来的。
我见过很多刚开始做二次开发的人,一上来就想着遍历模型取所有对象,然后取属性,结果发现重量取不到、零件重复出现、分类怎么都对不上,根因就是没搞清楚数据分了三层。你读模型的时候,API面对的是一个对象化的世界:Model对象代表当前模型,装配体对象代表构件,零件对象代表单个件。属性要从零件对象上取,位置要从装配关系里推,工程量则要调用计算类的方法,三者完全不是一回事。
2.2 为什么从“当前模型取对象”比“遍历数据库”更实用
Tekla二次开发读取零件信息,最常见的模型对象连接方式是GetObject()。这个方法打开的是“当前打开的模型”,不是让你去解析某个文件或数据库。它的好处是和你屏幕上看到的模型完全一致,包括了当前的视图过滤、选择过滤状态。这一点既是便利也是坑,后面章节会专门讲视图过滤带来的数据缺失问题。
2.3 装配层级:读零件必须面对的父子结构
Tekla的数据模型里,装配体是顶层的逻辑单元,一个装配体里可以有主零件、次零件,也可以嵌套子装配体。零件信息读取最常见的需求,是“按构件输出零件清单”,这时你必须先枚举装配体,再遍历装配体里的零件,而不能直接全局遍历所有零件对象。直接全局遍历的问题在于:你会在装配体层级和零件层级各看到同一根型钢一次,因为API列表里它既是装配体的对象成员,也是一个独立的零件对象。
我的习惯是:先按装配体走,取装配体标记和名称,然后遍历装配体里的零件;只有需要全模型材料汇总时才做全局零件遍历,并且先判断对象的类型再处理。这样输出的清单逻辑和图纸报表是一致的,别人拿去对图号时不会对不上。
3. Tekla二次开发读取零件信息:环境搭建与最小读取代码
3.1 环境准备与引用配置
做Tekla二次开发,语言基本选C#,IDE用Visual Studio。第一步是在引用里加入Tekla的API程序集,常见的做法是引用Tekla.Structures.dll和Tekla.Structures.Model.dll。如果你的机器装的是Tekla Structures某个常见版本,这两个DLL通常位于安装目录下的nt\bin\或nt\bin\plugins\里。
还有两个容易翻车的细节需要提前说。第一个是目标框架,老版本Tekla的API面向.NET Framework,新版虽然开始兼容.NET Core,但为了省事,新项目我一般直接选.NET Framework 4.8或4.7.2。第二个是平台目标,很多人在x64系统上编译x86程序,一运行就报“BadImageFormatException”,所以项目平台的首选一定要改成x64。
3.2 最小读取代码:按类型过滤零件
using Tekla.Structures; using Tekla.Structures.Model; class PartInfoReader { public void Run() { // 连接当前打开的Tekla模型 Model model = new Model(); if (!model.GetConnectionStatus()) { System.Windows.Forms.MessageBox.Show("请先打开一个Tekla模型"); return; } // 遍历装配体层级,再进入零件层级 ModelObjectEnumerator assemblyEnum = model.GetModelObjectSelector().GetAllObjectsWithType(new Assembly().GetType()); while (assemblyEnum.MoveNext()) { Assembly assembly = assemblyEnum.Current as Assembly; if (assembly == null) continue; // 取装配体主零件作为构件级别的标识 Part mainPart = assembly.GetMainPart() as Part; if (mainPart == null) continue; // 遍历装配体下的全部子零件 ModelObjectEnumerator partEnum = assembly.GetSecondaries(); while (partEnum.MoveNext()) { Part part = partEnum.Current as Part; if (part != null) { // 这里就是读取零件信息并输出的位置 string name = part.Name; string profile = part.Profile.ProfileString; string material = part.Material.MaterialString; System.Console.WriteLine("零件号: " + part.PartNumber.PartNumberString); System.Console.WriteLine("名称: " + name + " 截面: " + profile + " 材质: " + material); } } } } }这段代码做的事情,是连接当前打开的Tekla模型,枚举所有装配体,再取每个装配体里的次零件,最终拿到零件号、名称、截面、材质四个基础字段。选择器的GetAllObjectsWithType会返回符合条件的模型对象枚举器,这里指定了装配体类型,避免把螺栓、焊缝这类非零件对象混进来。我一般会先跑这样一段最小代码验证环境通不通,再往上加字段和统计逻辑。
如果只需要取零件号、截面这类字段,上面的代码已经够用。但如果你在写完这个Demo后发现某些零件总是读不到,或者读到的数量和模型视图里看到的不一致,多半不是代码问题,而是装配体遍历时漏掉了“没有装配关系的独立零件”,后面避坑章节里会专门展开。
3.3 从零件对象拿到属性字段:GetValue与GetUserProperty的差别
基础字段可以直接从对象的属性上取,但像重量、体积这种计算属性,以及你自己在零件上写的自定义属性,需要用不同的方式读取。Tekla二次开发里,标准属性直接用对象的属性成员(比如part.Weight),自定义属性要用GetUserProperty()方法。
// 取零件重量、体积与自定义属性 double weight = 0, volume = 0; part.GetReportProperty("WEIGHT", ref weight); part.GetReportProperty("VOLUME", ref volume); string mark = ""; part.GetUserProperty("MY_MARK", ref mark); string moreInfo = ""; part.GetUserProperty("PART_COMMENT", ref moreInfo); System.Console.WriteLine("净重: " + weight + "kg 体积: " + volume + "m3 自定义标记: " + mark);这里把重量体积的读取方式写出来,是因为很多人会试图直接用part.Weight这个属性,但它在某些场景下返回的是0,尤其是零件没有保存或者重量尚未计算时。用GetReportProperty是让Tekla按报告引擎去计算重量,结果和图纸报告里看到的一致。
GetUserProperty是读取用户在属性面板或通过API写入的扩展字段。注意:自定义属性的名称必须和模型里定义的完全一致,包括大小写,否则会静默失败返回空字符串。这一点不少有经验的人也踩过:属性名在界面里看起来是“My_Mark”,代码里写“my_mark”,读出来就为空。
4. 让读到的零件信息能直接用:数据落表和写回零件
4.1 落表:按零件归组输出清单的常见做法
读取零件信息的最终目的,八成是为了输出Excel或CSV清单。很多人第一反应是用Office的COM组件直接生成Excel,但我建议在Tekla二次开发场景里尽量避免。原因有三:服务器或设计院环境不一定装了Excel;COM调用在批量循环里容易内存泄漏;Tekla运行时会话里频繁创建Excel进程,容易导致模型卡顿。
常见做法是把零部件数据先装进DataTable,再统一写成CSV,用户用Excel直接打开CSV就能筛选排序。如果团队确实需要xlsx格式,再单独写一步把CSV转成xlsx,或者只在本地安装了Excel的机器上用COM。下面给一段CSV导出的核心代码:
System.Data.DataTable dt = new System.Data.DataTable(); dt.Columns.Add("构件号"); dt.Columns.Add("零件号"); dt.Columns.Add("名称"); dt.Columns.Add("截面"); dt.Columns.Add("材质"); dt.Columns.Add("净重(kg)"); // 假设前面已经遍历了装配体和零件,这里填充数据行 foreach (Part part in partList) { double weight = 0; part.GetReportProperty("WEIGHT", ref weight); dt.Rows.Add(assemblyMark, part.PartNumber.PartNumberString, part.Name, part.Profile.ProfileString, part.Material.MaterialString, weight); } // 写CSV文件,UTF-8编码保证中文不乱码 using (var sw = new System.IO.StreamWriter(@"C:\part_list.csv", false, System.Text.Encoding.UTF8)) { foreach (System.Data.DataRow row in dt.Rows) { sw.WriteLine(string.Join(",", row.ItemArray)); } }这段代码的重点是:先把数据收集到内存表里,再一次性写出,不要在遍历模型的过程中直接操作文件。数据量不大时可以边遍历边写,但零件上千时,频繁打开文件流的性能损耗明显。CSV使用UTF-8带BOM编码是比较稳妥的选择,用Excel打开才不乱码,记事本打开也不乱。
4.2 落图:按零件信息批量写回模型做标记
比导出清单更进一步的做法,是把读取到的信息处理后写回零件,最常见的是批量标记“校验结果”“加工批次号”或“下料顺序号”。写回要用SetUserProperty或SetProperty,然后在模型里用模板或过滤器读取这些字段。下面这段代码演示怎么给零件批量写批次:
string batchAttribute = "BATCH_NO"; foreach (Part part in partList) { string currentOrder = ""; part.GetUserProperty(batchAttribute, ref currentOrder); if (string.IsNullOrEmpty(currentOrder)) { // 只给没有批次号的零件分配新批次 string newBatch = "P" + batchCounter++.ToString("D3"); part.SetUserProperty(batchAttribute, newBatch); part.Modify(); } }这种做法的核心价值在于:把数据处理的结果留在模型里,后续出图、提料、数控加工都能共享。许多人做钢结构深化管理时会用这个套路:先读取每个零件的重量和板厚,按阈值打上“需探伤”“需热处理”的标记,再让下游用过滤器筛选这些零件生成工艺单。SetUserProperty写完后必须调用Modify(),否则修改不会真正写入模型,界面里刷新也不一定看得见。这是个很典型且代价不小的坑。
4.3 增量更新:只读“变化过的零件”以提升效率
随着模型迭代,每次刷新清单都要重跑全量读取,零件多了以后耗时明显。有个务实技巧是:利用Tekla的模型修改标记机制,记录上一次遍历时零件对象的Identifier.ID,下次只处理新增或ID列表之外变更过的零件。
常见做法是给零件写一个“上次读取版本”的自定义属性,每次读取时对比版本标记。比如第一次读取时给零件写入LAST_READ = "2024-06-01",第二次运行时先取该字段,等于“2024-06-01”的就跳过。这种基于自定义属性的增量方案实现简单,不需要依赖事件回调,在模型不大但迭代频繁的场景里够用。
5. Tekla二次开发读取零件信息的5个常见坑与排查
5.1 属性读取返回空值:字段名拼写与大小写
现象:代码运行不报错,但读取出来的截面、材质或自定义属性是空字符串。 原因:Tekla的属性名是大小写敏感且固定的,常见的有PROFILE和MATERIAL。自定义属性名如果带了空格、中文或大小写差异,读取会静默失败。 解决:先用Tekla的“属性面板”和“报告”功能确认字段准确名称,再在代码里硬编码或做成配置项。排查时可以在读取后立即判断IsEmpty并输出一条警告,不要等到输出清单时才发现一堆空列。
5.2 读到的零件比模型里少了:视图或选择过滤生效了
现象:代码遍历模型的零件数量,和模型文件夹里看到的零件数对不上,少了半截。 原因:GetModelObjectSelector()返回的对象受当前模型视图“选择过滤”的影响。如果某个视图中过滤掉了某些对象类型,枚举器可能直接不返回它们。 解决:读数据前先检查当前视图的过滤状态,或者在代码里显式创建自己的选择过滤器,只按类型筛选。我的做法是在工具启动时记录当前过滤状态,执行读取前临时关闭过滤,读完再恢复,避免影响用户正在使用的视图设置。
5.3 零件数量翻倍或统计重复:装配线遍历导致重复计数
现象:输出清单总行数比模型里Part对象多,同一根零件出现两次。 原因:直接全局遍历零件对象时,装配线(Assembly作为参数对象)和零件对象存在一对多关系;有些装配体对象本身也参与枚举,导致同一零件的不同引用形式被重复读到。 解决:统一以装配体为主线,只通过GetMainPart()和GetSecondaries()取零件;如果确实要做全局零件汇总,每次读取都先判断对象是否属于Part类型,并且用一个包含零件标识ID的哈希集合去重。
5.4 重量和模型报告对不上:单位系统与属性取值方式
现象:代码取出的重量与模型自带报告里的差了几百倍。 原因:Tekla的模型单位支持公制和英制,API读取的重量值在公制下是kg,如果模型设置成了英制或其他单位体系,数值含义完全不同;另外重量有净重、毛重、组立件重之分,字段选择不同结果也不同。 解决:读取前统一设置单位。最简单的方式是通过Tekla.Structures.ModelInternal工具或者使用GetReportProperty按报告字段取,它在内部会返回当前单位体系下的显示值。如果代码取数和报表分别用不同方式取,数值不一致就要优先检查单位设置。
5.5 程序运行后模型卡死或莫名崩溃:对象修改后未调用Modify
现象:批量写回时程序跑到中途就异常终止,或者模型出现未保存的诡异状态。 原因:只调用了SetUserProperty而没调用Modify(),或者修改对象后没有重新插入模型,导致内部缓存状态不一致;更常见的是在一次无限循环里反复读取同一个枚举器,造成API内部锁等待。 解决:每次写回属性后立即调用part.Modify(),并且对枚举器的循环增加最大迭代次数保护。遍历期间不要手动在Tekla界面里切换视图或选择对象,等程序跑完再操作模型。
6. 从“能用”到“顺手”:把零件读取脚本做成团队工具的三个进阶技巧
6.1 做成宏按钮,一键导出零件清单
把上面的逻辑封装成一个类库,生成DLL后放到Tekla的宏目录里,在菜单里挂一个按钮,用户点一下就能导出当前模型的零件清单。关键在于程序的入口尽量避免弹窗,参数可以从配置文件读取,这样对不熟悉代码的同事也友好。
6.2 校验零件信息完整性:缺失属性自动预警
零件信息读取的用途不止是导出,也可以反向校验模型质量。我习惯在工具里加一步:遍历零件,凡是没有材质、截面值或自定义批次号为空的,单独输出一个“待处理零件清单”。这个做法在模型交付前的自检环节非常有效,能一次性揪出所有漏填属性的零件。
6.3 把读取逻辑接入交付流程:从模型到清单的自动化
更进一步的做法,是把读取工具嵌入到项目交付的标准流程里:模型保存后自动运行读取,输出CSV并比对前一次输出,把差异部分生成变更说明。虽然自动化需要额外处理Tekla启动时的运行环境,但实现后节省的时间相当可观。
做这套工具下来,我最大的教训就是:不要把属性字段名硬编码得到处都是,维护一套字段配置表会省很多事;也不要信任一次读取的返回值,关键字段做二次校验。希望帮到你。
本文还有配套的精品资源,点击获取