Skip to content

สร้าง Ribbon (UI)

หลังจากเราเขียน command พื้นฐานรอดแล้ว ความท้าทายต่อไปคือการสร้าง Ribbon Tab และ Button ของตัวเอง เพื่อให้ผู้ใช้กดใช้งานได้สะดวกและดูเป็นมืออาชีพมากขึ้น

IExternalApplication คืออะไร?

Section titled “IExternalApplication คืออะไร?”

ในบทที่ผ่านมา เราใช้ IExternalCommand ในการรันคำสั่งเมื่อผู้ใช้กดปุ่ม แต่สำหรับรหัสที่เกี่ยวกับการ “วาดหน้าจอ (UI)” เราต้องใช้ IExternalApplication

IExternalApplication เป็นคลาสที่จะถูก Revit เรียกขึ้นมา 1 ครั้ง ตอนที่โปรแกรม Revit เพิ่งถูกเปิดขึ้นมา ซึ่งนี่คือจังหวะเวลาเดียวที่เราสามารถเข้าไปแทรกหน้าต่างและแท็บของเราลงในโปรแกรม Revit ได้

สร้างไฟล์ใหม่ชื่อ App.cs และใส่โค้ดชุดนี้ลงไป:

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 มีคลาสและอินเทอร์เฟซที่เราเรียกใช้ดังนี้ครับ:

  • มันคืออะไร: อินเทอร์เฟซหลักสำหรับการลงทะเบียนปลั๊กอินประเภทรันถาวรระบบเบหลังบ้าน
  • หน้าที่ในโค้ด: กำหนดให้คลาสมีจุดเริ่มต้น 2 จังหวะเวลาคือ OnStartup (เริ่มวาดเครื่องมือตอนเปิดเครื่อง) และ OnShutdown (เคลียร์หน่วยความจำตอนปิดโปรแกรม)
  • ทำไมต้องใช้: หากเราใช้เพียง IExternalCommand ปุ่มเราจะไม่มีทางไปโผล่อยู่บนแถบเครื่องมือหลักแบบอัตโนมัติได้ ต้องใช้ IExternalApplication ในการสถาปนาแท็บและปุ่มขึ้นมา
  • มันคืออะไร: อ็อบเจกต์ตัวแทนแอปพลิเคชัน Revit ที่เข้ามาควบคุมเฉพาะส่วนหน้าจอ (UI)
  • หน้าที่ในโค้ด: ทำหน้าที่เป็นกล่องเครื่องมือหลักให้เราเรียกใช้คำสั่งเพื่อปรับโครงสร้างหน้าต่างของ Revit เช่น การสร้างแท็บใหม่ คอยดักฟังผู้ใช้เปลี่ยนสลับหน้าต่าง หรือยัดพาเนลปุ่มกดลงไป
  • ทำไมต้องระวัง: ในช่วงเปิดหน้าจอนี้ เอกสารไฟล์งานยังไม่ได้ถูกโหลดขึ้นมาสำเร็จ ดังนั้น UIControlledApplication จึงไม่มีสิทธิ์ในการแก้ไขหรือดึงข้อมูลวัตถุโมเดลใดๆ ทั้งสิ้น (เน้นจัดการความสวยงามภายนอกเท่านั้น)

3. CreateRibbonTab และ CreateRibbonPanel (Methods)

Section titled “3. CreateRibbonTab และ CreateRibbonPanel (Methods)”
  • ทำหน้าที่อะไร:
    • CreateRibbonTab: สร้างแท็บเปล่าด้านบนขึ้นมาใหม่ เช่น ชื่อ "RevitToolkit"
    • CreateRibbonPanel: สร้างบล็อกกลุ่มย่อยภายในแท็บนั้น (เช่น Panel ชื่อ "General") เพื่อทำหน้าที่เป็นที่อยู่อาศัยของปุ่มกด
  • ทำไมต้องจัดโครงสร้างแบบนี้: ระบบของ Revit กำหนดไว้ชัดเจนว่าห้ามนำปุ่มกดไปแปะไว้ที่ตัว Tab หลักตรงๆ ปุ่มทุกปุ่มจะต้องพึ่งพาอาศัยอยู่ภายในตัว Panel ย่อยเสมอครับ
  • มันคืออะไร: พิมพ์เขียวความต้องการของปุ่มกด (Button Spec Blueprint)
  • หน้าที่ในโค้ด: เก็บรวบรวมคุณสมบัติทั้งหมดที่จำเป็นของปุ่มเอาไว้ ได้แก่:
    • name: รหัสอ้างอิงภายในระบบสำหรับ C# (ห้ามซ้ำกัน)
    • text: ข้อความตัวอักษรที่จะไปแสดงผลใต้ปุ่มจริงบนหน้าจอ
    • assemblyName: ชี้เป้าตำแหน่งไฟล์ .dll ที่เก็บรหัสโปรแกรม
    • className: คลาสปลายทางของคำสั่ง (FullClassName) ที่จะถูกเรียกใช้เมื่อผู้ใช้คลิกปุ่ม
  • ทำไมต้องใช้: เป็นขั้นตอนการทำข้อตกลงเตรียมข้อมูลปุ่มให้เรียบร้อยก่อนส่งให้ Panel วาดปุ่มจริงขึ้นมา
  • มันคืออะไร: อ็อบเจกต์ตัวแทนปุ่มจริงที่เกิดขึ้นบนริบบอนแล้ว
  • หน้าที่ในโค้ด: ให้เราปรับจูนความสวยงามและเงื่อนไขเพิ่มเติมของปุ่ม เช่น การผูกรูปภาพไอคอนขนาดใหญ่ผ่านพร็อพเพอร์ตี้ .LargeImage หรือใส่คำแนะนำช่วยเหลือผู้ใช้เมื่อเอาเมาส์จ่อด้วย .ToolTip

💡 การใส่รูปภาพไอคอนให้ปุ่ม (Icon)

Section titled “💡 การใส่รูปภาพไอคอนให้ปุ่ม (Icon)”

จากโค้ดด้านบนใน ข้อ 4. เราได้มีการโหลดรูปภาพด้วยกลุ่มคำสั่ง BitmapImage หากคุณต้องการให้ปุ่มมีรูปภาพ ให้ทำตามนี้ครับ:

  1. หารูปภาพนามสกุล .png สวยๆ ขนาด 32x32 pixel

  2. สร้างโฟลเดอร์ชื่อ icon ในโปรเจ็กต์ นำรูปภาพตั้งชื่อว่า icon.png จับโยนใส่เข้าไปในโฟลเดอร์ icon ของคุณ

  3. กดย่อโปรเจ็กต์คลิกที่รูปภาพ และไปดูที่หน้า Properties (มุมขวาล่าง) ให้ตั้งค่า Copy to Output Directory เป็น Copy if newer เพื่ออนุญาตให้รูปก็อปปี้เข้าไปแนบข้างๆ ไฟล์ .dll อัตโนมัติเวลาเรากด Build

  4. ⚠️ สำคัญมาก: โมดูล 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 เล็กน้อยดังนี้:

  1. เปลี่ยน Type จาก Command เป็น Application
  2. ลบแท็ก <Text> (เฉพาะ Command เท่านั้นที่มี Text)
  3. เปลี่ยน FullClassName ให้ชี้ไปที่ App
โปรเจ็กต์.addin
<?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” ที่เราสร้างไว้ครับ!