สร้าง Ribbon (UI)
หลังจากเราเขียน command พื้นฐานรอดแล้ว ความท้าทายต่อไปคือการสร้าง Ribbon Tab และ Button ของตัวเอง เพื่อให้ผู้ใช้กดใช้งานได้สะดวกและดูเป็นมืออาชีพมากขึ้น
IExternalApplication คืออะไร?
Section titled “IExternalApplication คืออะไร?”ในบทที่ผ่านมา เราใช้ IExternalCommand ในการรันคำสั่งเมื่อผู้ใช้กดปุ่ม แต่สำหรับรหัสที่เกี่ยวกับการ “วาดหน้าจอ (UI)” เราต้องใช้ IExternalApplication
IExternalApplication เป็นคลาสที่จะถูก Revit เรียกขึ้นมา 1 ครั้ง ตอนที่โปรแกรม Revit เพิ่งถูกเปิดขึ้นมา ซึ่งนี่คือจังหวะเวลาเดียวที่เราสามารถเข้าไปแทรกหน้าต่างและแท็บของเราลงในโปรแกรม Revit ได้
1. เขียนคลาส App
Section titled “1. เขียนคลาส App”สร้างไฟล์ใหม่ชื่อ App.cs และใส่โค้ดชุดนี้ลงไป:
using System;using System.IO;using System.Reflection;using System.Windows.Media.Imaging;using Autodesk.Revit.UI;
namespace RevitToolkit;
public class App : IExternalApplication{ public Result OnStartup(UIControlledApplication application) { try { // 1. สร้าง Ribbon Tab กลาง string tabName = "RevitToolkit"; application.CreateRibbonTab(tabName);
// 2. สร้าง Ribbon Panel เอาไว้ใส่ปุ่ม string panelName = "General"; RibbonPanel ribbonPanel = application.CreateRibbonPanel(tabName, panelName);
// 3. เตรียมข้อมูลของปุ่ม (ระบุชื่อปุ่ม และเส้นทางที่ชี้ไปยัง Command ที่ต้องการให้รันเมื่อกด) string thisAssemblyPath = Assembly.GetExecutingAssembly().Location; string assemblyFolder = Path.GetDirectoryName(thisAssemblyPath) ?? string.Empty;
PushButtonData buttonData = new PushButtonData( name: "cmdHelloWorld", text: "Hello\nRevit", assemblyName: thisAssemblyPath, className: "RevitToolkit.Command" ) { ToolTip = "Show a HelloWorld dialog." };
// 4. ใส่ภาพรูปภาพให้ปุ่ม (LargeImage มักใช้ 32x32 px) string iconPath = Path.Combine(assemblyFolder, "icon", "icon.png"); if (File.Exists(iconPath)) { buttonData.LargeImage = new BitmapImage(new Uri(iconPath)); }
// 5. นำปุ่มยัดใส่ลง Panel RibbonItem ribbonItem = ribbonPanel.AddItem(buttonData); if (ribbonItem is not PushButton pushButton) { throw new InvalidOperationException("Failed to create the push button."); }
return Result.Succeeded; } catch (Exception ex) { TaskDialog.Show("Error on Startup", ex.Message); return Result.Failed; } }
public Result OnShutdown(UIControlledApplication application) { return Result.Succeeded; }}🔍 ชำแหละโครงสร้าง Revit Ribbon UI API
Section titled “🔍 ชำแหละโครงสร้าง Revit Ribbon UI API”ในการสร้างหน้าตาส่วนติดต่อผู้ใช้งานระดับพรีเมียมบน Revit มีคลาสและอินเทอร์เฟซที่เราเรียกใช้ดังนี้ครับ:
1. IExternalApplication (Interface)
Section titled “1. IExternalApplication (Interface)”- มันคืออะไร: อินเทอร์เฟซหลักสำหรับการลงทะเบียนปลั๊กอินประเภทรันถาวรระบบเบหลังบ้าน
- หน้าที่ในโค้ด: กำหนดให้คลาสมีจุดเริ่มต้น 2 จังหวะเวลาคือ
OnStartup(เริ่มวาดเครื่องมือตอนเปิดเครื่อง) และOnShutdown(เคลียร์หน่วยความจำตอนปิดโปรแกรม) - ทำไมต้องใช้: หากเราใช้เพียง
IExternalCommandปุ่มเราจะไม่มีทางไปโผล่อยู่บนแถบเครื่องมือหลักแบบอัตโนมัติได้ ต้องใช้IExternalApplicationในการสถาปนาแท็บและปุ่มขึ้นมา
2. UIControlledApplication (Class)
Section titled “2. UIControlledApplication (Class)”- มันคืออะไร: อ็อบเจกต์ตัวแทนแอปพลิเคชัน Revit ที่เข้ามาควบคุมเฉพาะส่วนหน้าจอ (UI)
- หน้าที่ในโค้ด: ทำหน้าที่เป็นกล่องเครื่องมือหลักให้เราเรียกใช้คำสั่งเพื่อปรับโครงสร้างหน้าต่างของ Revit เช่น การสร้างแท็บใหม่ คอยดักฟังผู้ใช้เปลี่ยนสลับหน้าต่าง หรือยัดพาเนลปุ่มกดลงไป
- ทำไมต้องระวัง: ในช่วงเปิดหน้าจอนี้ เอกสารไฟล์งานยังไม่ได้ถูกโหลดขึ้นมาสำเร็จ ดังนั้น
UIControlledApplicationจึงไม่มีสิทธิ์ในการแก้ไขหรือดึงข้อมูลวัตถุโมเดลใดๆ ทั้งสิ้น (เน้นจัดการความสวยงามภายนอกเท่านั้น)
3. CreateRibbonTab และ CreateRibbonPanel (Methods)
Section titled “3. CreateRibbonTab และ CreateRibbonPanel (Methods)”- ทำหน้าที่อะไร:
CreateRibbonTab: สร้างแท็บเปล่าด้านบนขึ้นมาใหม่ เช่น ชื่อ"RevitToolkit"CreateRibbonPanel: สร้างบล็อกกลุ่มย่อยภายในแท็บนั้น (เช่น Panel ชื่อ"General") เพื่อทำหน้าที่เป็นที่อยู่อาศัยของปุ่มกด
- ทำไมต้องจัดโครงสร้างแบบนี้: ระบบของ Revit กำหนดไว้ชัดเจนว่าห้ามนำปุ่มกดไปแปะไว้ที่ตัว Tab หลักตรงๆ ปุ่มทุกปุ่มจะต้องพึ่งพาอาศัยอยู่ภายในตัว Panel ย่อยเสมอครับ
4. PushButtonData (Class)
Section titled “4. PushButtonData (Class)”- มันคืออะไร: พิมพ์เขียวความต้องการของปุ่มกด (Button Spec Blueprint)
- หน้าที่ในโค้ด: เก็บรวบรวมคุณสมบัติทั้งหมดที่จำเป็นของปุ่มเอาไว้ ได้แก่:
name: รหัสอ้างอิงภายในระบบสำหรับ C# (ห้ามซ้ำกัน)text: ข้อความตัวอักษรที่จะไปแสดงผลใต้ปุ่มจริงบนหน้าจอassemblyName: ชี้เป้าตำแหน่งไฟล์.dllที่เก็บรหัสโปรแกรมclassName: คลาสปลายทางของคำสั่ง (FullClassName) ที่จะถูกเรียกใช้เมื่อผู้ใช้คลิกปุ่ม
- ทำไมต้องใช้: เป็นขั้นตอนการทำข้อตกลงเตรียมข้อมูลปุ่มให้เรียบร้อยก่อนส่งให้ Panel วาดปุ่มจริงขึ้นมา
5. PushButton (Class)
Section titled “5. PushButton (Class)”- มันคืออะไร: อ็อบเจกต์ตัวแทนปุ่มจริงที่เกิดขึ้นบนริบบอนแล้ว
- หน้าที่ในโค้ด: ให้เราปรับจูนความสวยงามและเงื่อนไขเพิ่มเติมของปุ่ม เช่น การผูกรูปภาพไอคอนขนาดใหญ่ผ่านพร็อพเพอร์ตี้
.LargeImageหรือใส่คำแนะนำช่วยเหลือผู้ใช้เมื่อเอาเมาส์จ่อด้วย.ToolTip
💡 การใส่รูปภาพไอคอนให้ปุ่ม (Icon)
Section titled “💡 การใส่รูปภาพไอคอนให้ปุ่ม (Icon)”จากโค้ดด้านบนใน ข้อ 4. เราได้มีการโหลดรูปภาพด้วยกลุ่มคำสั่ง BitmapImage หากคุณต้องการให้ปุ่มมีรูปภาพ ให้ทำตามนี้ครับ:
-
หารูปภาพนามสกุล
.pngสวยๆ ขนาด 32x32 pixel -
สร้างโฟลเดอร์ชื่อ
iconในโปรเจ็กต์ นำรูปภาพตั้งชื่อว่าicon.pngจับโยนใส่เข้าไปในโฟลเดอร์iconของคุณ -
กดย่อโปรเจ็กต์คลิกที่รูปภาพ และไปดูที่หน้า Properties (มุมขวาล่าง) ให้ตั้งค่า Copy to Output Directory เป็น
Copy if newerเพื่ออนุญาตให้รูปก็อปปี้เข้าไปแนบข้างๆ ไฟล์.dllอัตโนมัติเวลาเรากด Build -
⚠️ สำคัญมาก: โมดูล
BitmapImageเป็นเทคโนโลยีของหน้าต่าง WPF หากพิมพ์ตามแล้วเจอปัญหาหาไฟล์ไม่เจอหรือเกิด Error สีแดงตอนคอมไพล์ ให้เข้าไปแก้ไขไฟล์โปรเจ็กต์.csprojโดยการแทรกแท็ก<UseWPF>true</UseWPF>เข้าไปในกลุ่ม<PropertyGroup>แรกสุดของไฟล์ (วางไว้ใต้<Nullable>enable</Nullable>) ดังตัวอย่างนี้ครับ:โปรเจ็กต์.csproj <Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net8.0-windows</TargetFramework><Nullable>enable</Nullable><!-- 🟢 เพิ่มบรรทัดนี้ลงไปเพื่อเปิดใช้งาน WPF --><UseWPF>true</UseWPF><ImplicitUsings>enable</ImplicitUsings><PlatformTarget>x64</PlatformTarget></PropertyGroup>...</Project>
2. ปรับปรุงไฟล์ .addin
Section titled “2. ปรับปรุงไฟล์ .addin”เนื่องจากจุดเริ่มต้น (Entry Point) ของ Plugin เราเปลี่ยนจากตัว Command มาเป็น Application วาดหน้าจอ เราจึงต้องแก้ไข RevitToolkit.addin เล็กน้อยดังนี้:
- เปลี่ยน
TypeจากCommandเป็นApplication - ลบแท็ก
<Text>(เฉพาะ Command เท่านั้นที่มี Text) - เปลี่ยน
FullClassNameให้ชี้ไปที่App
<?xml version="1.0" encoding="utf-8" standalone="no"?><RevitAddIns> <!-- เปลี่ยน Type เป็น Application --> <AddIn Type="Application"> <Name>RevitToolkit</Name> <Assembly>RevitToolkit\RevitToolkit.dll</Assembly> <AddInId>YOUR-NEW-GUID-HERE</AddInId> <!-- ให้ชี้ไปที่คลาส Application แทน --> <FullClassName>RevitToolkit.App</FullClassName> <VendorId>PREMIX</VendorId> <VendorDescription>Premix Company</VendorDescription> </AddIn></RevitAddIns>หลังจากนั้น สั่ง Build (.NET) ใหม่อีกครั้ง และลองเปิด Revit 2026 ขึ้นมา คุณจะเห็นแท็บ “RevitToolkit” เกิดขึ้นด้านบนพร้อม panel “General” และปุ่ม “Hello Revit” ที่เราสร้างไว้ครับ!