























LightCAD的插件系统允许开发者扩展平台功能,插件可以:
LightCAD启动
│
▼
扫描插件目录
│
▼
加载程序集(DLL)
│
▼
反射查找ILcPlugin实现
│
▼
创建插件实例
│
▼
依次调用生命周期方法
public interface ILcPlugin
{
/// <summary>
/// 插件加载时调用
/// </summary>
void Loaded();
/// <summary>
/// UI初始化时调用
/// </summary>
void InitUI();
/// <summary>
/// 完成所有初始化后调用
/// </summary>
void Completed();
/// <summary>
/// 文档运行时初始化时调用
/// </summary>
void OnInitializeDocRt(DocumentRuntime docRt);
/// <summary>
/// 文档运行时销毁时调用
/// </summary>
void OnDisposeDocRt(DocumentRuntime docRt);
}
using LightCAD.Core;
using LightCAD.Runtime;
namespace MyFirstPlugin
{
public class MyFirstPlugin : ILcPlugin
{
public void Loaded()
{
Console.WriteLine("MyFirstPlugin: Loaded");
}
public void InitUI()
{
Console.WriteLine("MyFirstPlugin: InitUI");
}
public void Completed()
{
Console.WriteLine("MyFirstPlugin: Completed");
}
public void OnInitializeDocRt(DocumentRuntime docRt)
{
Console.WriteLine($"MyFirstPlugin: Document initialized - {docRt.Document.Name}");
}
public void OnDisposeDocRt(DocumentRuntime docRt)
{
Console.WriteLine($"MyFirstPlugin: Document disposed - {docRt.Document.Name}");
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Reference Include="LightCAD.Core">
<HintPath>..\Libs\LightCAD.Core.dll</HintPath>
</Reference>
<Reference Include="LightCAD.Runtime">
<HintPath>..\Libs\LightCAD.Runtime.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
LightCAD使用Ribbon风格的选项卡界面,主要结构如下:
TabItem (选项卡)
├── Name # 内部名称
├── Text # 显示文本
├── ShortcutKey # 快捷键
└── ButtonGroups # 按钮组列表
└── TabButtonGroup
└── Buttons # 按钮列表
└── TabButton
├── Name # 命令名称
├── Text # 显示文本
├── Icon # 图标
├── IsCommand # 是否为命令
├── Width # 宽度
└── DropDowns # 下拉菜单
public static TabItem MyTabItem = new TabItem
{
Name = "MyTab",
Text = "我的工具",
ShortcutKey = "ALT-M",
ButtonGroups = new List<TabButtonGroup>
{
new TabButtonGroup
{
Buttons = new List<TabButton>
{
new TabButton
{
Name = "MyCommand1",
Text = "功能一",
Icon = LoadIcon("icon1.png"),
IsCommand = true,
},
new TabButton
{
Name = "MyCommand2",
Text = "功能二",
Icon = LoadIcon("icon2.png"),
IsCommand = true,
Width = 80,
}
}
}
}
};
// 在Completed()中注册
public void Completed()
{
AppRuntime.UISystem.AddInitTabItems([MyTabItem]);
}
new TabButton
{
Name = "DrawTools",
Text = "绘图工具",
Icon = Properties.Resources.DrawIcon,
IsCommand = true,
DropDowns = new List<TabButton>
{
new TabButton
{
Name = "DrawLine",
Text = "画线",
Icon = Properties.Resources.LineIcon,
IsCommand = true,
},
new TabButton
{
Name = "DrawCircle",
Text = "画圆",
Icon = Properties.Resources.CircleIcon,
IsCommand = true,
},
new TabButton
{
Name = "DrawRect",
Text = "画矩形",
Icon = Properties.Resources.RectIcon,
IsCommand = true,
},
}
}
使用Resources.resx管理图标:
Properties/Resources.resx// 自动生成的访问器
Icon = Properties.Resources.MyIcon
或者从文件加载:
private static System.Drawing.Image LoadIcon(string path)
{
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream($"MyPlugin.Resources.{path}");
return System.Drawing.Image.FromStream(stream);
}
using LightCAD.Runtime;
namespace MyPlugin
{
[CommandClass]
public class MyCommands
{
// 命令方法将在这里定义
}
}
[CommandMethod(Name = "Hello", ShortCuts = "HI")]
public CommandResult HelloWorld(IDocumentEditor docEditor, string[] args)
{
// 获取命令控制器
var cmdCtrl = docEditor.GetCommandController();
// 输出消息
cmdCtrl.WriteInfo("Hello, LightCAD World!");
return CommandResult.Succ();
}
[CommandMethod(Name = "SetColor", ShortCuts = "SC")]
public CommandResult SetColor(IDocumentEditor docEditor, string[] args)
{
if (args.Length < 1)
{
docEditor.GetCommandController().WriteError("请指定颜色参数");
return CommandResult.Fail("缺少参数");
}
var colorName = args[0];
// 处理颜色设置...
return CommandResult.Succ();
}
[CommandMethod(Name = "LongTask", ShortCuts = "LT")]
public CommandResult LongTask(IDocumentEditor docEditor, string[] args)
{
Task.Run(async () =>
{
var cmdCtrl = docEditor.GetCommandController();
cmdCtrl.WriteInfo("开始长时间任务...");
await Task.Delay(3000); // 模拟耗时操作
cmdCtrl.WriteInfo("任务完成!");
});
return CommandResult.Succ();
}
// 成功结果
CommandResult.Succ()
// 失败结果
CommandResult.Fail("错误信息")
// 带数据的结果
new CommandResult
{
Status = CommandStatus.Success,
Data = someData
}
[CommandMethod(Name = "DocInfo")]
public CommandResult GetDocInfo(IDocumentEditor docEditor, string[] args)
{
var docRt = docEditor.GetDocumentRuntime();
var doc = docRt.Document;
var cmdCtrl = docEditor.GetCommandController();
cmdCtrl.WriteInfo($"文档名称: {doc.Name}");
cmdCtrl.WriteInfo($"元素数量: {doc.Elements.Count}");
cmdCtrl.WriteInfo($"图层数量: {doc.Layers.Count}");
return CommandResult.Succ();
}
[CommandMethod(Name = "ListElements")]
public CommandResult ListElements(IDocumentEditor docEditor, string[] args)
{
var doc = docEditor.GetDocumentRuntime().Document;
var cmdCtrl = docEditor.GetCommandController();
foreach (var element in doc.Elements)
{
cmdCtrl.WriteInfo($"元素: {element.Type.Name} - {element.Id}");
}
return CommandResult.Succ();
}
// 获取或创建图层
private LcLayer GetOrCreateLayer(LcDocument doc, string layerName, int color)
{
var layer = doc.Layers.FirstOrDefault(l => l.Name == layerName);
if (layer == null)
{
layer = doc.CreateObject<LcLayer>();
layer.Name = layerName;
layer.Color = color;
layer.SetLineType(new LcLineType("ByLayer"));
layer.Transparency = 0;
doc.Layers.Add(layer);
}
return layer;
}
// 使用示例
var layer = GetOrCreateLayer(doc, "MyLayer", 0xFF0000); // 红色图层
element.Layer = layer.Name;
[CommandMethod(Name = "GetPoint")]
public async CommandResult GetPoint(IDocumentEditor docEditor, string[] args)
{
var pointInputer = new PointInputer(docEditor);
// 获取单个点
var result = await pointInputer.Execute("请指定点:");
if (result.Status == InputStatus.OK)
{
var point = result.Point;
docEditor.GetCommandController().WriteInfo($"点坐标: ({point.X}, {point.Y})");
}
else if (result.Status == InputStatus.Cancel)
{
docEditor.GetCommandController().WriteInfo("操作已取消");
}
return CommandResult.Succ();
}
[CommandMethod(Name = "GetPoints")]
public async CommandResult GetPoints(IDocumentEditor docEditor, string[] args)
{
var pointInputer = new PointInputer(docEditor);
var points = new List<Vector2>();
var cmdCtrl = docEditor.GetCommandController();
cmdCtrl.WriteInfo("指定第一个点:");
var result = await pointInputer.Execute();
while (result.Status == InputStatus.OK)
{
points.Add(result.Point);
cmdCtrl.WriteInfo($"已添加点 {points.Count}: ({result.Point.X}, {result.Point.Y})");
result = await pointInputer.Execute("指定下一点或按ESC完成:");
}
cmdCtrl.WriteInfo($"共获取 {points.Count} 个点");
return CommandResult.Succ();
}
[CommandMethod(Name = "GetText")]
public async CommandResult GetText(IDocumentEditor docEditor, string[] args)
{
var textInputer = new CmdTextInputer(docEditor);
var result = await textInputer.Execute("请输入文本:");
if (!string.IsNullOrEmpty(result))
{
docEditor.GetCommandController().WriteInfo($"输入的文本: {result}");
}
return CommandResult.Succ();
}
[CommandMethod(Name = "SelectElements")]
public async CommandResult SelectElements(IDocumentEditor docEditor, string[] args)
{
var elementInputer = new ElementSetInputer(docEditor);
var result = await elementInputer.Execute("请选择元素:");
if (result?.ValueX != null)
{
var elements = result.ValueX as List<LcElement>;
var cmdCtrl = docEditor.GetCommandController();
cmdCtrl.WriteInfo($"选择了 {elements.Count} 个元素");
foreach (var ele in elements)
{
cmdCtrl.WriteInfo($" - {ele.Type.DispalyName}");
}
}
return CommandResult.Succ();
}
// 创建直线
var line = doc.CreateObject<LcLine>();
line.Start = new Vector2(0, 0);
line.End = new Vector2(100, 100);
line.Layer = "0";
vportRt.ActiveElementSet.InsertElement(line);
// 创建多段线
var polyline = doc.CreateObject<LcPolyLine>();
polyline.AddVertex(new Vector2(0, 0));
polyline.AddVertex(new Vector2(100, 0));
polyline.AddVertex(new Vector2(100, 100));
polyline.IsClosed = true;
vportRt.ActiveElementSet.InsertElement(polyline);
// 创建圆
var circle = doc.CreateObject<LcCircle>();
circle.Center = new Vector2(50, 50);
circle.Radius = 25;
vportRt.ActiveElementSet.InsertElement(circle);
[CommandMethod(Name = "CreateLawn")]
public CommandResult CreateLawn(IDocumentEditor docEditor, string[] args)
{
var docRt = docEditor.GetDocumentRuntime();
var doc = docRt.Document;
var vportRt = docRt.ActiveViewportRuntime;
// 获取组件定义
var lawnDef = docRt.GetUseComDef("场布施工设计.绿色文明", "草坪", null) as QdLawnDef;
// 创建草坪元素
var lawn = new QdLawn(lawnDef);
lawn.Initilize(doc);
// 设置轮廓
var poly = new Polyline2d();
poly.AddCurve(new Line2d(new Vector2(0, 0), new Vector2(100, 0)));
poly.AddCurve(new Line2d(new Vector2(100, 0), new Vector2(100, 100)));
poly.AddCurve(new Line2d(new Vector2(100, 100), new Vector2(0, 100)));
poly.AddCurve(new Line2d(new Vector2(0, 100), new Vector2(0, 0)));
lawn.Outline = poly;
// 设置其他属性
lawn.ResetBoundingBox();
lawn.Layer = "Layout_Lawn";
lawn.Bottom = 0;
// 插入到文档
vportRt.ActiveElementSet.InsertElement(lawn);
return CommandResult.Succ();
}
public void OnInitializeDocRt(DocumentRuntime docRt)
{
var doc = docRt.Document;
// 订阅元素添加事件
doc.ElementAdded += (sender, e) =>
{
Console.WriteLine($"元素已添加: {e.Element.Type.Name}");
};
// 订阅元素删除事件
doc.ElementRemoved += (sender, e) =>
{
Console.WriteLine($"元素已删除: {e.Element.Type.Name}");
};
// 订阅元素修改事件
doc.ElementChanged += (sender, e) =>
{
Console.WriteLine($"元素已修改: {e.Element.Type.Name}");
};
}
public void OnInitializeDocRt(DocumentRuntime docRt)
{
// 订阅选择变化事件
docRt.SelectionChanged += (sender, e) =>
{
var selectedElements = docRt.GetSelectedElements();
Console.WriteLine($"选择了 {selectedElements.Count} 个元素");
};
}
// 创建Settings类
public class MyPluginSettings
{
private static readonly string SettingsPath =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"LightCAD", "MyPlugin", "settings.json");
public string DefaultLayer { get; set; } = "0";
public int DefaultColor { get; set; } = 0xFFFFFF;
public double DefaultLineWidth { get; set; } = 1.0;
public static MyPluginSettings Load()
{
if (File.Exists(SettingsPath))
{
var json = File.ReadAllText(SettingsPath);
return JsonConvert.DeserializeObject<MyPluginSettings>(json);
}
return new MyPluginSettings();
}
public void Save()
{
var dir = Path.GetDirectoryName(SettingsPath);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
var json = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(SettingsPath, json);
}
}
public class MyPlugin : ILcPlugin
{
private static MyPluginSettings Settings;
public void Loaded()
{
Settings = MyPluginSettings.Load();
}
public void OnDisposeDocRt(DocumentRuntime docRt)
{
Settings.Save();
}
}
[CommandMethod(Name = "SafeCommand")]
public CommandResult SafeCommand(IDocumentEditor docEditor, string[] args)
{
try
{
// 可能抛出异常的代码
DoSomething();
return CommandResult.Succ();
}
catch (ArgumentException ex)
{
docEditor.GetCommandController().WriteError($"参数错误: {ex.Message}");
return CommandResult.Fail(ex.Message);
}
catch (Exception ex)
{
docEditor.GetCommandController().WriteError($"发生错误: {ex.Message}");
// 记录详细日志
LogError(ex);
return CommandResult.Fail(ex.Message);
}
}
[CommandMethod(Name = "ValidatedInput")]
public async CommandResult ValidatedInput(IDocumentEditor docEditor, string[] args)
{
var cmdCtrl = docEditor.GetCommandController();
var pointInputer = new PointInputer(docEditor);
// 获取第一个点
var result1 = await pointInputer.Execute("指定第一个点:");
if (result1.Status != InputStatus.OK)
{
cmdCtrl.WriteInfo("操作已取消");
return CommandResult.Succ();
}
// 获取第二个点,并验证不能与第一个点相同
Vector2 point2;
while (true)
{
var result2 = await pointInputer.Execute("指定第二个点:");
if (result2.Status != InputStatus.OK)
{
cmdCtrl.WriteInfo("操作已取消");
return CommandResult.Succ();
}
point2 = result2.Point;
if (point2.DistanceTo(result1.Point) > 0.001)
break;
cmdCtrl.WriteError("第二个点不能与第一个点重合,请重新输入");
}
// 继续处理...
return CommandResult.Succ();
}
using System;
using System.Collections.Generic;
using LightCAD.Core;
using LightCAD.Runtime;
using LightCAD.Drawing;
using LightCAD.MathLib;
namespace MyCompletePlugin
{
/// <summary>
/// 完整的插件示例
/// </summary>
public class MyCompletePlugin : ILcPlugin
{
public static TabItem MyTabItem = new TabItem
{
Name = "MyTools",
Text = "我的工具",
ShortcutKey = "ALT-Y",
ButtonGroups = new List<TabButtonGroup>
{
new TabButtonGroup
{
Buttons = new List<TabButton>
{
new TabButton
{
Name = "DrawRectangle",
Text = "绘制矩形",
IsCommand = true,
},
new TabButton
{
Name = "ShowInfo",
Text = "显示信息",
IsCommand = true,
}
}
}
}
};
public void Loaded()
{
Console.WriteLine("MyCompletePlugin loaded");
}
public void InitUI()
{
// UI初始化
}
public void Completed()
{
AppRuntime.UISystem.AddInitTabItems([MyTabItem]);
}
public void OnInitializeDocRt(DocumentRuntime docRt)
{
Console.WriteLine($"Document initialized: {docRt.Document.Name}");
}
public void OnDisposeDocRt(DocumentRuntime docRt)
{
Console.WriteLine($"Document disposed: {docRt.Document.Name}");
}
}
[CommandClass]
public class MyCommands
{
[CommandMethod(Name = "DrawRectangle", ShortCuts = "DR")]
public async CommandResult DrawRectangle(IDocumentEditor docEditor, string[] args)
{
var cmdCtrl = docEditor.GetCommandController();
var pointInputer = new PointInputer(docEditor);
// 获取第一个角点
cmdCtrl.WriteInfo("指定矩形第一个角点:");
var result1 = await pointInputer.Execute();
if (result1.Status != InputStatus.OK)
return CommandResult.Succ();
// 获取对角点
cmdCtrl.WriteInfo("指定矩形对角点:");
var result2 = await pointInputer.Execute();
if (result2.Status != InputStatus.OK)
return CommandResult.Succ();
// 创建矩形
var docRt = docEditor.GetDocumentRuntime();
var doc = docRt.Document;
var vportRt = docRt.ActiveViewportRuntime;
var p1 = result1.Point;
var p2 = result2.Point;
var polyline = doc.CreateObject<LcPolyLine>();
polyline.AddVertex(new Vector2(p1.X, p1.Y));
polyline.AddVertex(new Vector2(p2.X, p1.Y));
polyline.AddVertex(new Vector2(p2.X, p2.Y));
polyline.AddVertex(new Vector2(p1.X, p2.Y));
polyline.IsClosed = true;
vportRt.ActiveElementSet.InsertElement(polyline);
cmdCtrl.WriteInfo("矩形创建成功");
return CommandResult.Succ();
}
[CommandMethod(Name = "ShowInfo", ShortCuts = "SI")]
public CommandResult ShowInfo(IDocumentEditor docEditor, string[] args)
{
var docRt = docEditor.GetDocumentRuntime();
var doc = docRt.Document;
var cmdCtrl = docEditor.GetCommandController();
cmdCtrl.WriteInfo("=== 文档信息 ===");
cmdCtrl.WriteInfo($"文档名称: {doc.Name}");
cmdCtrl.WriteInfo($"元素总数: {doc.Elements.Count}");
cmdCtrl.WriteInfo($"图层数量: {doc.Layers.Count}");
cmdCtrl.WriteInfo("================");
return CommandResult.Succ();
}
}
}
本章介绍了LightCAD插件开发的基础知识:
掌握这些基础知识后,下一章我们将深入学习元素类型系统的详细实现。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。