การแก้ไขโมเดล (Transaction)
การกระทำใดๆ ก็ตามที่ส่งผลให้ฐานข้อมูลของ Revit เปลี่ยนแปลง (เช่น สร้างผนัง, ขยับเสา, เปลี่ยนค่าพารามิเตอร์) จำเป็นต้องทำภายใต้ระบบ Transaction เสมอ ถ้าไม่ทำ Revit จะเด้ง Error โค้ดพังทันที!
กฎ 3 ข้อของ Transaction
Section titled “กฎ 3 ข้อของ Transaction”- ชื่อเดียวใจเดียว: 1 Transaction ต้องระบุชื่อเสมอรอบรับการทำ Undo ของผู้ใช้
- Start & Commit: ต้องเริ่มเปิดตัวด้วย
.Start()และปิดจบงานด้วย.Commit() - TransactionMode.Manual: ด้านบนสุดของคลาส
Commandต้องกำกับไว้เป็นManual(อย่างที่เราทำมาตลอด)
ตัวอย่าง: การเขียนค่า Parameter อัตโนมัติลงในเสา (Column)
Section titled “ตัวอย่าง: การเขียนค่า Parameter อัตโนมัติลงในเสา (Column)”สมมติว่าเราต้องการเขียนเครื่องหมาย (Mark) เรียงเบอร์ให้กับ “เสา” ทั้งหมดในโมเดล เช่น C-1, C-2, C-3 เพื่อให้สอดคล้องกับบทที่แล้วที่เราเรียนดึงข้อมูลเสากันมาครับ
ไปแก้ไขโค้ดใน 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 มีระบบความมั่นคงของข้อมูลที่เข้มงวดมาก เรามาวิเคราะห์คลาสและเมธอดที่เราเรียกใช้งานกันครับ:
1. Transaction (Class)
Section titled “1. Transaction (Class)”- มันคืออะไร: คลาสควบคุมช่วงเวลาการเปลี่ยนแปลงข้อมูลแบบจำลอง
- หน้าที่ในโค้ด: เปรียบเสมือนด่านกักกันความปลอดภัยเพื่อบันทึกประวัติการกระทำ โดยมี 3 คำสั่งศักดิ์สิทธิ์ที่ห้ามละเลยคือ:
trans.Start(): สัญญาณเปิดประตูรับบันทึกการแก้ไขข้อมูลโมเดลtrans.Commit(): ยืนยันการเปลี่ยนแปลงทั้งหมดเพื่อบันทึกอย่างถาวรลงในฐานข้อมูล และสร้างประวัติสำหรับปุ่ม Undo ใน Revittrans.RollBack(): หากเกิด Error หรือทำงานขัดแย้งกลางคัน คำสั่งนี้จะทำการยกเลิกการปรับปรุงก่อนหน้าทั้งหมดทันทีเพื่อความปลอดภัยของข้อมูล
- ทำไมต้องใช้: โครงสร้างข้อมูล BIM มีความเชื่อมโยงสูง หากเราแก้ไขข้อมูลโดยปราศจาก Transaction ระบบ Revit จะเด้งพังทันทีเพื่อเซฟตัวไฟล์งานไม่ให้เสียหายกึ่งกลาง
2. Parameter (Class)
Section titled “2. Parameter (Class)”- มันคืออะไร: ช่องตัวแปรคุณสมบัติหรือเมทาดาตา (Metadata) ประจำตัววัตถุแต่ละชิ้น
- หน้าที่ในโค้ด: ทำหน้าที่เข้าถึงช่องกรอกข้อมูล เช่น ขนาด, รหัสแบรนด์, หรือในตัวอย่างนี้คือช่องพารามิเตอร์รหัสหมายเลขวัตถุ (
Mark) - คำสั่งย่อยที่สำคัญ:
.Set(value): คำสั่งเขียนข้อมูลใหม่บันทึกทับลงไปในพารามิเตอร์ ซึ่งรองรับทั้งตัวเลข ทศนิยม และข้อความ (string).IsReadOnly: พร็อพเพอร์ตี้ตรวจสอบสถานะสิทธิ์ หากพารามิเตอร์ชิ้นนั้นถูกล็อกโดยระบบ (เช่น ความยาวคานที่คำนวณจากโมเดลหลัก) เราจะไม่สามารถใช้คำสั่ง.Set()เพื่อเขียนทับได้ ดังนั้นเราต้องตรวจสอบให้ดีก่อนเขียน
3. BuiltInParameter (Enum)
Section titled “3. BuiltInParameter (Enum)”- มันคืออะไร: ลิสต์รหัสอ้างอิงพารามิเตอร์พื้นฐานที่ Autodesk ผูกติดมากับระบบภายใน Revit
- หน้าที่ในโค้ด: ชี้เฉพาะเจาะจงไปยังพารามิเตอร์เป้าหมาย เช่น
BuiltInParameter.ALL_MODEL_MARKชี้ไปยังพารามิเตอร์"Mark"ของวัตถุชิ้นนั้นๆ - ทำไมต้องใช้: เป็นหัวใจของการพัฒนาปลั๊กอินสากล เพราะช่วยตัดปัญหาสะกดชื่อพารามิเตอร์ผิดพลาด และป้องกันปัญหาโปรแกรม Revit ต่างภาษา (เช่น ใน Revit ภาษาไทยอาจจะมองเห็นพารามิเตอร์เป็นคำอื่น แต่หลังบ้านจะยังรันผ่านรหัส
ALL_MODEL_MARKตัวเดียวกันเสมอ)
ข้อผิดพลาดที่เจอบ่อยที่สุดสำหรับมือใหม่คือ ลืมใส่ trans.Commit() ซึ่งจะทำให้โค้ดรันจนจบแบบนิ่งๆ ไม่ error แต่ดันไม่มีอะไรเปลี่ยนแปลงในโมเดลเลย!
พอเข้าใจเรื่องของ FilteredElementCollector การดึงข้อมูล และการจัดการด้วย Transaction แล้ว คุณก็พร้อมลุยกับงานอัตโนมัติต่างๆ เต็มตัวได้แล้วครับ