Examples
ชุดตัวอย่างหน้านี้เป็นแบบ command-first สำหรับลองรันคำสั่งเดี่ยว ๆ ก่อน
ถ้าโปรเจ็กต์ของคุณเปลี่ยนไปใช้ Ribbon เองแล้ว ให้ใช้ App.cs แบบ IExternalApplication และ .addin แบบ Type="Application" แทน
📁 โครงสร้างโฟลเดอร์โปรเจ็กต์ปลั๊กอิน (Structural Project Directory Structure)
Section titled “📁 โครงสร้างโฟลเดอร์โปรเจ็กต์ปลั๊กอิน (Structural Project Directory Structure)”เพื่อให้เห็นภาพโครงสร้างโปรเจ็กต์ปลั๊กอินที่ถูกต้อง และนำชิ้นส่วนโค้ดต่างๆ ไปวางไว้ได้ถูกตำแหน่งโดยไม่ต้องสับสนเดาตำแหน่งโฟลเดอร์ ให้ผู้เรียนทำการจัดระเบียบสร้างไฟล์และโฟลเดอร์ตามแผนผังไดเรกทอรีนี้ครับ:
BIMToolkit/ # 1. โฟลเดอร์นอกสุด (Root Folder) ที่เปิดใช้งานบนเครื่องมือเขียนโค้ด│├── .vscode/ # โฟลเดอร์คอนฟิกดีบั๊กสำหรับ Visual Studio Code (VS Code)│ └── launch.json # 🟢 ไฟล์ตั้งค่าดีบั๊กสั่งเปิด Revit.exe บน VS Code (F5)│└── BIMToolkit/ # 2. โฟลเดอร์โปรเจ็กต์หลัก (C# Class Library) │ ├── icon/ # โฟลเดอร์เก็บสื่อและรูปภาพสำหรับทำริบบอนปุ่มกราฟิก │ └── icon.png # 🟢 รูปภาพไอคอนของปุ่มปลั๊กอิน (ขนาดที่แนะนำคือ 32x32 px) │ ├── Properties/ # โฟลเดอร์เก็บคุณสมบัติของ Visual Studio รุ่นใหญ่ (VS 2022) │ └── launchSettings.json # 🟢 ไฟล์ตั้งค่าดีบั๊กสั่งเปิด Revit.exe บน Visual Studio (F5) │ ├── bin/ # โฟลเดอร์รวบรวมผลลัพธ์การ Build สำเร็จ (ระบบจะสร้างให้อัตโนมัติ) │ └── Debug/ │ └── net8.0-windows/ │ ├── BIMToolkit.dll │ └── BIMToolkit.pdb │ ├── obj/ # โฟลเดอร์พักข้อมูลระหว่างทำการคอมไพล์ (ระบบจะสร้างให้อัตโนมัติ) │ ├── App.cs # 🟢 โค้ดสร้างริบบอนเมนูบนแถบเครื่องมือ (IExternalApplication) ├── Command.cs # 🟢 โค้ดคำสั่งทำงานหลักของปุ่มปลั๊กอิน (IExternalCommand) ├── BIMToolkit.csproj # 🟢 ไฟล์ตั้งค่าคอนฟิกตัวแปรและการอ้างอิง DLL ของตัวโปรเจ็กต์ C# └── BIMToolkit.addin # 🟢 ไฟล์ Manifest ลงทะเบียนปลั๊กอินเข้าสู่ฐานข้อมูลระบบของ RevitCommand.cs
Section titled “Command.cs”using Autodesk.Revit.Attributes;using Autodesk.Revit.DB;using Autodesk.Revit.UI;
namespace RevitToolkit;
// 1. ระบุโหมดการจัดการ Transaction แบบดึงมือ[Transaction(TransactionMode.Manual)]public class Command : IExternalCommand{ // 2. ขาเข้า (Entry point) ที่ Revit จะวิ่งมาหา public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { // 3. แสดงแบนเนอร์หรือ UI เริ่มต้น TaskDialog.Show("RevitToolkit", "Hello from Revit 2026");
// 4. ส่งสถานะว่างานจบสมบูรณ์ (ถ้าไม่ใส่บรรทัดนี้ หรือ Error โปรแกรมใน Revit จะพ่นเตือน) return Result.Succeeded; }}RevitToolkit.csproj
Section titled “RevitToolkit.csproj”<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <!-- กำหนดตัวรันเป็น .NET 8 (สำหรับ Revit 2026) หรือแก้ไขเป็น net10.0-windows ถ้าใช้ 2027 และล็อค x64 --> <TargetFramework>net8.0-windows</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <PlatformTarget>x64</PlatformTarget>
<!-- ตัวแปรช่วยเรื่อง Deploy เพื่อไม่ให้ยาวเกินไป --> <RevitYear>2026</RevitYear> <RevitAddinDir>$(ProgramData)\Autodesk\Revit\Addins\$(RevitYear)\</RevitAddinDir> <DeployDir>D:\code\Visual Studio\Revit\RevitToolkit\deploy\Revit\$(RevitYear)\RevitToolkit\</DeployDir> </PropertyGroup>
<ItemGroup> <!-- ห้าม Copy API DLL ลงโฟลเดอร์ปลายทางเด็ดขาด (Private=false) --> <Reference Include="RevitAPI"> <HintPath>C:\Program Files\Autodesk\Revit 2026\RevitAPI.dll</HintPath> <Private>false</Private> </Reference> <Reference Include="RevitAPIUI"> <HintPath>C:\Program Files\Autodesk\Revit 2026\RevitAPIUI.dll</HintPath> <Private>false</Private> </Reference> </ItemGroup>
<!-- คำสั่งอัตโนมัติให้คัดลอกไฟล์ .dll และ .addin ไปยังเป้าหมายหลังจากกด Build (F6) ทันที --> <Target Name="CopyRevitFiles" AfterTargets="Build"> <MakeDir Directories="$(RevitAddinDir)" /> <MakeDir Directories="$(DeployDir)" />
<Copy SourceFiles="$(TargetPath)" DestinationFiles="$(DeployDir)$(TargetFileName)" />
<Copy SourceFiles="$(ProjectDir)RevitToolkit.addin" DestinationFiles="$(RevitAddinDir)RevitToolkit.addin" /> </Target></Project>RevitToolkit.addin
Section titled “RevitToolkit.addin”<?xml version="1.0" encoding="utf-8" standalone="no"?><RevitAddIns> <!-- ให้เรียก Command แบบเพียวๆ ไม่วาด UI เพิ่ม --> <AddIn Type="Command"> <Name>RevitToolkit</Name> <!-- ตำแหน่งของ DLL อ้างอิงจากโฟลเดอร์ที่เราใช้ทดสอบ (จากไฟล์ .csproj) --> <Assembly>D:\code\Visual Studio\Revit\RevitToolkit\deploy\Revit\2026\RevitToolkit\RevitToolkit.dll</Assembly> <!-- เปลี่ยนรหัสให้ไม่ซ้ำกับคนอื่น --> <AddInId>YOUR-NEW-GUID-HERE</AddInId> <FullClassName>RevitToolkit.Command</FullClassName> <!-- ปุ่มจะโชว์ว่า RevitToolkit --> <Text>RevitToolkit</Text> <VendorId>PREMIX</VendorId> <VendorDescription>Premix Company</VendorDescription> </AddIn></RevitAddIns>สร้าง GUID ใหม่ทุกครั้งก่อนเอาไปใช้จริง เช่นผ่าน Tools > Create GUID ใน Visual Studio
launchSettings.json
Section titled “launchSettings.json”{ "profiles": { "Revit 2026": { "commandName": "Executable", "executablePath": "C:\\Program Files\\Autodesk\\Revit 2026\\Revit.exe", "workingDirectory": "C:\\Program Files\\Autodesk\\Revit 2026\\" } }}🚀 สูตรสำเร็จโค้ดใช้งานจริงในอุตสาหกรรม (Production Boilerplates)
Section titled “🚀 สูตรสำเร็จโค้ดใช้งานจริงในอุตสาหกรรม (Production Boilerplates)”ด้านล่างนี้คือตัวอย่างโปรแกรมสำเร็จรูปเกรดสูง (High-value automation templates) ที่รวบรวมทักษะเรื่อง Element Collector, Transaction, Parameter และการสืบค้นพิกัดชิ้นวัตถุเข้าไว้ด้วยกัน ผู้อ่านสามารถนำไปประยุกต์ใช้งานจริงในบริษัทได้ทันทีครับ:
📊 สูตรที่ 1: ระบบส่งออกปริมาณเสาและพิกัดงานออกมาเป็น Excel (BIM CSV Exporter)
Section titled “📊 สูตรที่ 1: ระบบส่งออกปริมาณเสาและพิกัดงานออกมาเป็น Excel (BIM CSV Exporter)”สคริปต์นี้จะสแกนหาเสาโครงสร้างทั้งหมดในโครงการ ดึงชื่อประเภท (Family Type), พิกัดตำแหน่ง XYZ และขนาดมิติจริง จากนั้นส่งออกข้อมูลจัดลงไฟล์ตารางข้อความ CSV ซึ่งสามารถนำไปเปิดทำงานต่อใน Microsoft Excel เพื่อคำนวณปริมาณงานและทำราคา (BOQ) ได้ทันที:
using System;using System.IO;using System.Text;using Autodesk.Revit.Attributes;using Autodesk.Revit.DB;using Autodesk.Revit.UI;
namespace RevitToolkit;
[Transaction(TransactionMode.Manual)]public class ColumnCsvExporter : IExternalCommand{ public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { Document doc = commandData.Application.ActiveUIDocument.Document;
try { // 1. ดึงเสาโครงสร้างทั้งหมดในแบบจำลอง var columns = new FilteredElementCollector(doc) .OfCategory(BuiltInCategory.OST_StructuralColumns) .WhereElementIsNotElementType() .ToElements();
if (columns.Count == 0) { TaskDialog.Show("Exporter", "ไม่พบวัตถุเสาโครงสร้างในโครงการนี้"); return Result.Succeeded; }
// 2. ตั้งเป้าหมายบันทึกไฟล์ข้อความ CSV ไปยัง Desktop ของผู้ใช้ string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); string csvFilePath = Path.Combine(desktopPath, "Revit_Column_QTO.csv");
// 3. เริ่มสร้างชุดข้อความตาราง (ใช้ UTF-8 BOM เพื่อไม่ให้ภาษาไทยใน Excel แสดงผลเพี้ยน) StringBuilder csvBuilder = new StringBuilder(); csvBuilder.AppendLine("\uFEFFElement ID,Family Type,Position X,Position Y,Level,Mark");
foreach (Element col in columns) { // ดึงพารามิเตอร์ชื่อประเภทและเลเวลชั้นที่อยู่ของเสา string typeName = col.Name; string levelName = doc.GetElement(col.LevelId)?.Name ?? "N/A";
// ดึงรหัสเบอร์เขียนข้อความ (Mark) Parameter markParam = col.get_Parameter(BuiltInParameter.ALL_MODEL_MARK); string markValue = markParam?.AsString() ?? "";
// ดึงพิกัดตำแหน่งภูมิศาสตร์ XYZ ทางกายภาพของเสา string positionStr = "N/A,N/A"; if (col.Location is LocationPoint locPoint) { XYZ point = locPoint.Point;
// แปลงค่าหน่วยวัดมาตรฐาน Revit (ฟุต) ให้กลายเป็นระบบเมตร // ใช้ UnitUtils.ConvertFromInternalUnits() แทนการคูณเลขตรงๆ เพื่อให้ถูกต้องตามมาตรฐาน Revit API double posX = UnitUtils.ConvertFromInternalUnits(point.X, UnitTypeId.Meters); double posY = UnitUtils.ConvertFromInternalUnits(point.Y, UnitTypeId.Meters); positionStr = $"{posX:F3},{posY:F3}"; }
// ต่อสายอักษรบันทึกตารางข้อมูล csvBuilder.AppendLine($"{col.Id},{typeName},{positionStr},{levelName},{markValue}"); }
// 4. บันทึกไฟล์ข้อมูลลงดิสก์ File.WriteAllText(csvFilePath, csvBuilder.ToString(), Encoding.UTF8);
TaskDialog.Show("Success", $"ส่งออกข้อมูลปริมาณเสาทั้งหมด {columns.Count} ต้น สำเร็จ!\nบันทึกไฟล์ไว้ที่: {csvFilePath}"); return Result.Succeeded; } catch (Exception ex) { TaskDialog.Show("Error", $"ระบบส่งออกข้อมูลล้มเหลว: {ex.Message}"); return Result.Failed; } }}🏷️ สูตรที่ 2: ระบบเปลี่ยนชื่อรหัสเสายกแผงเรียงลำดับจากซ้ายไปขวา (Grid-based Batch Rename)
Section titled “🏷️ สูตรที่ 2: ระบบเปลี่ยนชื่อรหัสเสายกแผงเรียงลำดับจากซ้ายไปขวา (Grid-based Batch Rename)”บ่อยครั้งที่โมเดลที่เราดึงมาจากฝ่ายสถาปัตย์จะมีชื่อพารามิเตอร์ Mark สะเปะสะปะ สคริปต์นี้จะรันค้นหาเสาโครงสร้างทั้งหมด คัดแยกเลเวลชั้นงาน จากนั้นคำนวณเรียงลำดับพิกัดจากซ้ายไปขวา (ตามแกน X) และบนลงล่าง (ตามแกน Y) แล้วเขียนรหัสใหม่ เช่น C-01, C-02, C-03 จัดระเบียบให้อย่างเรียบร้อยสมบูรณ์แบบ:
using System;using System.Collections.Generic;using System.Linq;using Autodesk.Revit.Attributes;using Autodesk.Revit.DB;using Autodesk.Revit.UI;
namespace RevitToolkit;
[Transaction(TransactionMode.Manual)]public class ColumnBatchRenamer : IExternalCommand{ public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { Document doc = commandData.Application.ActiveUIDocument.Document;
try { // 1. ดึงข้อมูลเสาทั้งหมด var columns = new FilteredElementCollector(doc) .OfCategory(BuiltInCategory.OST_StructuralColumns) .WhereElementIsNotElementType() .ToElements() .ToList();
if (!columns.Any()) { TaskDialog.Show("Batch Rename", "ไม่พบเสาโครงสร้างสำหรับการจัดลำดับเรียงรหัสใหม่"); return Result.Succeeded; }
// 2. จัดกลุ่มเสาตามระดับเลเวลชั้น และเรียงพิกัด (X ก่อน ตามด้วย Y) // เพื่อให้การรันรหัสเสาเรียงลำดับจากซ้ายไปขวา บนลงล่าง อย่างสม่ำเสมอ var sortedColumns = columns .Where(c => c.Location is LocationPoint) .OrderBy(c => c.LevelId.Value) // เรียงตามระดับความสูงชั้น .ThenBy(c => ((LocationPoint)c.Location).Point.X) // เรียงพิกัดซ้ายไปขวา .ThenByDescending(c => ((LocationPoint)c.Location).Point.Y) // เรียงพิกัดบนลงล่าง .ToList();
// 3. เริ่มเปิดประวัติ Transaction เพื่ออัปเดตโมเดล using (Transaction trans = new Transaction(doc, "Batch Rename Columns by Grid")) { trans.Start();
int counter = 1; foreach (Element col in sortedColumns) { Parameter markParam = col.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);
if (markParam != null && !markParam.IsReadOnly) { // สั่งเขียนรหัสเรียงลำดับ เช่น C-01, C-02, C-03 เป็นต้น string newMarkValue = $"C-{counter:D2}"; markParam.Set(newMarkValue); counter++; } }
trans.Commit(); }
TaskDialog.Show("Success", $"ระบบสแกนและทำการเปลี่ยนรหัสชื่อเสาเรียงลำดับใหม่สำเร็จ {sortedColumns.Count} ต้น!"); return Result.Succeeded; } catch (Exception ex) { TaskDialog.Show("Error", $"ระบบจัดลำดับรหัสเสาล้มเหลว: {ex.Message}"); return Result.Failed; } }}