Skip to content

การแก้ไขโมเดล (Transaction)

การกระทำใดๆ ก็ตามที่ส่งผลให้ฐานข้อมูลของ Revit เปลี่ยนแปลง (เช่น สร้างผนัง, ขยับเสา, เปลี่ยนค่าพารามิเตอร์) จำเป็นต้องทำภายใต้ระบบ Transaction เสมอ ถ้าไม่ทำ Revit จะเด้ง Error โค้ดพังทันที!

  1. ชื่อเดียวใจเดียว: 1 Transaction ต้องระบุชื่อเสมอรอบรับการทำ Undo ของผู้ใช้
  2. Start & Commit: ต้องเริ่มเปิดตัวด้วย .Start() และปิดจบงานด้วย .Commit()
  3. TransactionMode.Manual: ด้านบนสุดของคลาส Command ต้องกำกับไว้เป็น Manual (อย่างที่เราทำมาตลอด)

ตัวอย่าง: การเขียนค่า Parameter อัตโนมัติลงในเสา (Column)

Section titled “ตัวอย่าง: การเขียนค่า Parameter อัตโนมัติลงในเสา (Column)”

สมมติว่าเราต้องการเขียนเครื่องหมาย (Mark) เรียงเบอร์ให้กับ “เสา” ทั้งหมดในโมเดล เช่น C-1, C-2, C-3 เพื่อให้สอดคล้องกับบทที่แล้วที่เราเรียนดึงข้อมูลเสากันมาครับ

ไปแก้ไขโค้ดใน Command.cs ใหม่อีกครั้ง:

Command.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 Command : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
Document doc = commandData.Application.ActiveUIDocument.Document;
// 1. ดึงเสาทั้งหมดมาก่อน (อิงโค้ดจากบทก่อนหน้าเป๊ะๆ)
var columns = new FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_StructuralColumns)
.WhereElementIsNotElementType()
.ToElements();
if (columns.Count == 0)
{
TaskDialog.Show("RevitToolkit", "ไม่พบเสาในโมเดล");
return Result.Succeeded;
}
// 2. ใช้โครงสร้าง using สร้าง Transaction ใหม่ขึ้นมา
using (Transaction trans = new Transaction(doc, "Auto Number Columns"))
{
try
{
// 3. เริ่มเปิดงาน
trans.Start();
int counter = 1;
foreach (Element column in columns)
{
// 4. เข้าถึงพารามิเตอร์ "Mark" (หมายเลข)
Parameter markParam = column.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);
if (markParam != null && !markParam.IsReadOnly)
{
// 5. เขียนค่ากลับเข้าไปในพารามิเตอร์
markParam.Set($"C-{counter}");
counter++;
}
}
// 6. กดเซฟยืนยันคำสั่ง
trans.Commit();
TaskDialog.Show("RevitToolkit", $"อัปเดตหมายเลขเสาสำเร็จ {counter - 1} ต้น");
return Result.Succeeded;
}
catch (Exception ex)
{
// 7. หากมีอะไรผิดพลาด โค้ดจะตกมาหน้านี้ และสั่งลบล้าง (Rollback) สิ่งที่ทำค้างไว้
trans.RollBack();
message = ex.Message;
return Result.Failed;
}
}
}
}

🔍 ชำแหละระบบรักษาความปลอดภัยฐานข้อมูลและ API แก้ไขโมเดล

Section titled “🔍 ชำแหละระบบรักษาความปลอดภัยฐานข้อมูลและ API แก้ไขโมเดล”

การสั่งปรับปรุงฐานข้อมูลโมเดล Revit มีระบบความมั่นคงของข้อมูลที่เข้มงวดมาก เรามาวิเคราะห์คลาสและเมธอดที่เราเรียกใช้งานกันครับ:

  • มันคืออะไร: คลาสควบคุมช่วงเวลาการเปลี่ยนแปลงข้อมูลแบบจำลอง
  • หน้าที่ในโค้ด: เปรียบเสมือนด่านกักกันความปลอดภัยเพื่อบันทึกประวัติการกระทำ โดยมี 3 คำสั่งศักดิ์สิทธิ์ที่ห้ามละเลยคือ:
    • trans.Start(): สัญญาณเปิดประตูรับบันทึกการแก้ไขข้อมูลโมเดล
    • trans.Commit(): ยืนยันการเปลี่ยนแปลงทั้งหมดเพื่อบันทึกอย่างถาวรลงในฐานข้อมูล และสร้างประวัติสำหรับปุ่ม Undo ใน Revit
    • trans.RollBack(): หากเกิด Error หรือทำงานขัดแย้งกลางคัน คำสั่งนี้จะทำการยกเลิกการปรับปรุงก่อนหน้าทั้งหมดทันทีเพื่อความปลอดภัยของข้อมูล
  • ทำไมต้องใช้: โครงสร้างข้อมูล BIM มีความเชื่อมโยงสูง หากเราแก้ไขข้อมูลโดยปราศจาก Transaction ระบบ Revit จะเด้งพังทันทีเพื่อเซฟตัวไฟล์งานไม่ให้เสียหายกึ่งกลาง
  • มันคืออะไร: ช่องตัวแปรคุณสมบัติหรือเมทาดาตา (Metadata) ประจำตัววัตถุแต่ละชิ้น
  • หน้าที่ในโค้ด: ทำหน้าที่เข้าถึงช่องกรอกข้อมูล เช่น ขนาด, รหัสแบรนด์, หรือในตัวอย่างนี้คือช่องพารามิเตอร์รหัสหมายเลขวัตถุ (Mark)
  • คำสั่งย่อยที่สำคัญ:
    • .Set(value): คำสั่งเขียนข้อมูลใหม่บันทึกทับลงไปในพารามิเตอร์ ซึ่งรองรับทั้งตัวเลข ทศนิยม และข้อความ (string)
    • .IsReadOnly: พร็อพเพอร์ตี้ตรวจสอบสถานะสิทธิ์ หากพารามิเตอร์ชิ้นนั้นถูกล็อกโดยระบบ (เช่น ความยาวคานที่คำนวณจากโมเดลหลัก) เราจะไม่สามารถใช้คำสั่ง .Set() เพื่อเขียนทับได้ ดังนั้นเราต้องตรวจสอบให้ดีก่อนเขียน
  • มันคืออะไร: ลิสต์รหัสอ้างอิงพารามิเตอร์พื้นฐานที่ Autodesk ผูกติดมากับระบบภายใน Revit
  • หน้าที่ในโค้ด: ชี้เฉพาะเจาะจงไปยังพารามิเตอร์เป้าหมาย เช่น BuiltInParameter.ALL_MODEL_MARK ชี้ไปยังพารามิเตอร์ "Mark" ของวัตถุชิ้นนั้นๆ
  • ทำไมต้องใช้: เป็นหัวใจของการพัฒนาปลั๊กอินสากล เพราะช่วยตัดปัญหาสะกดชื่อพารามิเตอร์ผิดพลาด และป้องกันปัญหาโปรแกรม Revit ต่างภาษา (เช่น ใน Revit ภาษาไทยอาจจะมองเห็นพารามิเตอร์เป็นคำอื่น แต่หลังบ้านจะยังรันผ่านรหัส ALL_MODEL_MARK ตัวเดียวกันเสมอ)

ข้อผิดพลาดที่เจอบ่อยที่สุดสำหรับมือใหม่คือ ลืมใส่ trans.Commit() ซึ่งจะทำให้โค้ดรันจนจบแบบนิ่งๆ ไม่ error แต่ดันไม่มีอะไรเปลี่ยนแปลงในโมเดลเลย!

พอเข้าใจเรื่องของ FilteredElementCollector การดึงข้อมูล และการจัดการด้วย Transaction แล้ว คุณก็พร้อมลุยกับงานอัตโนมัติต่างๆ เต็มตัวได้แล้วครับ