Skip to content

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 ลงทะเบียนปลั๊กอินเข้าสู่ฐานข้อมูลระบบของ Revit

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;
}
}
โปรเจ็กต์.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>
โปรเจ็กต์.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

{
"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) ได้ทันที:

ColumnCsvExporter.cs
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 จัดระเบียบให้อย่างเรียบร้อยสมบูรณ์แบบ:

ColumnBatchRenamer.cs
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;
}
}
}