06-扩展方法与实用工具教程
第六章:扩展方法与实用工具教程
6.1 扩展方法设计理念
6.1.1 什么是扩展方法
扩展方法是C#的一个强大特性,它允许我们为现有类型添加新的方法,而无需修改原始类型的源代码或创建派生类型。在IFoxCAD中,扩展方法是实现功能扩展的主要方式。
扩展方法的基本语法:
public static class MyExtensions
{
// this关键字指定扩展的类型
public static void MyMethod(this SomeType obj)
{
// 实现代码
}
}
6.1.2 IFoxCAD扩展方法的组织
IFoxCAD的扩展方法按照目标类型组织在不同的文件中:
ExtensionMethod/
├── EditorEx.cs - Editor类的扩展
├── EntityEx/ - 各种图元的扩展
├── DatabaseEx.cs - Database类的扩展
├── DBObjectEx.cs - DBObject类的扩展
├── ObjectIdEx.cs - ObjectId的扩展
├── CollectionEx.cs - 集合类的扩展
├── SymbolTableEx.cs - 符号表的扩展
├── GeometryEx/ - 几何类的扩展
└── ...
6.1.3 扩展方法的优势
在CAD二次开发中使用扩展方法的优势:
- 代码简洁:直接在对象上调用,无需额外的工具类
- 链式调用:支持流畅的API设计
- 类型安全:编译时类型检查
- 易于发现:IDE智能提示自动显示可用方法
6.2 Editor扩展方法
6.2.1 输出与提示
IFoxCAD为Editor类提供了丰富的扩展方法:
using var tr = new DBTrans();
var ed = tr.Editor;
// 输出消息
ed?.WriteMessage("\n这是一条消息");
ed?.WriteMessageWithReturn("这条消息后面会换行");
// 显示警告框
ed?.Alert("这是一个警告!");
6.2.2 用户输入扩展
获取点
using var tr = new DBTrans();
var ed = tr.Editor;
// 获取单个点
var ptResult = ed?.GetPoint("\n请指定点:");
if (ptResult?.Status == PromptStatus.OK)
{
Point3d pt = ptResult.Value;
ed?.WriteMessage($"\n选择的点:{pt}");
}
// 获取点(带基点)
var basePoint = new Point3d(0, 0, 0);
var pt2Result = ed?.GetPoint("\n请指定第二点:", basePoint);
获取距离
using var tr = new DBTrans();
var ed = tr.Editor;
// 获取距离
var distResult = ed?.GetDistance("\n请指定距离:");
if (distResult?.Status == PromptStatus.OK)
{
double dist = distResult.Value;
ed?.WriteMessage($"\n距离:{dist}");
}
// 从基点获取距离
var distResult2 = ed?.GetDistance(new Point3d(0, 0, 0), "\n请指定第二点:");
获取角度
using var tr = new DBTrans();
var ed = tr.Editor;
// 获取角度
var angResult = ed?.GetAngle("\n请指定角度:");
if (angResult?.Status == PromptStatus.OK)
{
double angle = angResult.Value;
ed?.WriteMessage($"\n角度(弧度):{angle}");
ed?.WriteMessage($"\n角度(度):{angle * 180 / Math.PI}");
}
获取字符串
using var tr = new DBTrans();
var ed = tr.Editor;
// 获取字符串
var strResult = ed?.GetString("\n请输入文字:");
if (strResult?.Status == PromptStatus.OK)
{
string input = strResult.StringResult;
ed?.WriteMessage($"\n输入的文字:{input}");
}
// 允许空格
var options = new PromptStringOptions("\n请输入文字(可含空格):");
options.AllowSpaces = true;
var strResult2 = ed?.GetString(options);
获取关键字
using var tr = new DBTrans();
var ed = tr.Editor;
// 获取关键字选择
var options = new PromptKeywordOptions("\n请选择操作 [添加(A)/删除(D)/修改(M)]:");
options.Keywords.Add("Add", "添加", "添加(A)");
options.Keywords.Add("Delete", "删除", "删除(D)");
options.Keywords.Add("Modify", "修改", "修改(M)");
options.AllowNone = false;
var kwResult = ed?.GetKeywords(options);
if (kwResult?.Status == PromptStatus.OK)
{
switch (kwResult.StringResult)
{
case "Add":
ed?.WriteMessage("\n执行添加操作");
break;
case "Delete":
ed?.WriteMessage("\n执行删除操作");
break;
case "Modify":
ed?.WriteMessage("\n执行修改操作");
break;
}
}
6.2.3 图元获取扩展
using var tr = new DBTrans();
var ed = tr.Editor;
// 获取单个图元
var entResult = ed?.GetEntity("\n请选择图元:");
if (entResult?.Status == PromptStatus.OK)
{
ObjectId entId = entResult.ObjectId;
Point3d pickPt = entResult.PickedPoint;
var ent = tr.GetObject<Entity>(entId);
ed?.WriteMessage($"\n选择了:{ent?.GetType().Name}");
}
// 带过滤的选择
var filter = OpFilter.Build(e => e.Dxf(0) == "CIRCLE");
var options = new PromptEntityOptions("\n请选择圆:");
var result = ed?.GetEntity(options, filter);
6.2.4 嵌套图元选择
using var tr = new DBTrans();
var ed = tr.Editor;
// 选择嵌套在块内的图元
var nestedResult = ed?.GetNestedEntity("\n请选择嵌套图元:");
if (nestedResult?.Status == PromptStatus.OK)
{
ObjectId nestedId = nestedResult.ObjectId;
var containers = nestedResult.GetContainers();
ed?.WriteMessage($"\n嵌套层次:{containers.Length}");
}
6.3 DBObject扩展方法
6.3.1 打开模式扩展
IFoxCAD提供了便捷的打开模式控制:
using var tr = new DBTrans();
// 选择图元
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId);
if (ent == null) return;
// 使用ForWrite扩展方法
using (ent.ForWrite())
{
// 在这个块内可以修改图元
ent.Color = Color.FromColorIndex(ColorMethod.ByAci, 1);
}
// 退出块后自动恢复只读模式
6.3.2 克隆扩展
using var tr = new DBTrans();
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId);
if (ent == null) return;
// 克隆图元
var clone = ent.Clone() as Entity;
if (clone != null)
{
// 移动克隆的图元
clone.TransformBy(Matrix3d.Displacement(new Vector3d(100, 0, 0)));
// 添加到当前空间
tr.CurrentSpace.AddEntity(clone);
}
6.3.3 变换扩展
using var tr = new DBTrans();
var result = tr.Editor?.GetEntity("\n请选择图元:");
if (result?.Status != PromptStatus.OK) return;
var ent = tr.GetObject<Entity>(result.ObjectId, OpenMode.ForWrite);
if (ent == null) return;
// 移动
var displacement = new Vector3d(50, 50, 0);
ent.TransformBy(Matrix3d.Displacement(displacement));
// 旋转(绕点旋转45度)
var center = new Point3d(0, 0, 0);
var angle = Math.PI / 4; // 45度
ent.TransformBy(Matrix3d.Rotation(angle, Vector3d.ZAxis, center));
// 缩放
var scaleFactor = 2.0;
ent.TransformBy(Matrix3d.Scaling(scaleFactor, center));
// 镜像
var mirrorPt1 = new Point3d(0, 0, 0);
var mirrorPt2 = new Point3d(0, 100, 0);
var mirrorLine = new Line3d(mirrorPt1, mirrorPt2);
ent.TransformBy(Matrix3d.Mirroring(mirrorLine));
6.4 ObjectId扩展方法
6.4.1 有效性检查
using var tr = new DBTrans();
ObjectId someId = GetSomeObjectId();
// 检查ID是否有效
if (someId.IsOk())
{
var obj = tr.GetObject<DBObject>(someId);
// 使用对象...
}
// IsOk方法检查:
// - ID不为空
// - ID是有效的
// - 对象未被删除
6.4.2 快速获取对象
using var tr = new DBTrans();
// 选择图元
var result = tr.Editor?.GetSelection();
if (result?.Status != PromptStatus.OK) return;
// 使用扩展方法快速获取对象
foreach (SelectedObject obj in result.Value)
{
if (obj == null) continue;
// 直接从ObjectId获取类型化对象
var ent = obj.ObjectId.GetObject<Entity>(tr.Transaction);
if (ent != null)
{
tr.Editor?.WriteMessage($"\n{ent.GetType().Name}");
}
}
6.5 Collection扩展方法
6.5.1 ObjectIdCollection扩展
using var tr = new DBTrans();
// 创建ObjectIdCollection
ObjectIdCollection ids = new ObjectIdCollection();
// 从选择集获取ID集合
var result = tr.Editor?.GetSelection();
if (result?.Status == PromptStatus.OK)
{
foreach (SelectedObject obj in result.Value)
{
if (obj != null)
ids.Add(obj.ObjectId);
}
}
// 使用扩展方法转换为数组
ObjectId[] idArray = ids.ToArray();
// 使用LINQ(需要先转换为IEnumerable)
var lineIds = ids.Cast<ObjectId>()
.Where(id => tr.GetObject<Entity>(id) is Line)
.ToList();
6.5.2 Point3dCollection扩展
// 创建点集合
Point3dCollection points = new Point3dCollection
{
new Point3d(0, 0, 0),
new Point3d(100, 0, 0),
new Point3d(100, 100, 0),
new Point3d(0, 100, 0)
};
// 转换为数组
Point3d[] ptArray = points.Cast<Point3d>().ToArray();
// 转换为列表
List<Point3d> ptList = points.Cast<Point3d>().ToList();
// 计算边界
double minX = ptList.Min(p => p.X);
double maxX = ptList.Max(p => p.X);
double minY = ptList.Min(p => p.Y);
double maxY = ptList.Max(p => p.Y);
6.6 符号表扩展方法
6.6.1 块表扩展
using var tr = new DBTrans();
// 添加图元到当前空间
var line = new Line(Point3d.Origin, new Point3d(100, 100, 0));
tr.CurrentSpace.AddEntity(line);
// 添加多个图元
var entities = new Entity[]
{
new Circle(new Point3d(50, 50, 0), Vector3d.ZAxis, 25),
new Line(new Point3d(0, 0, 0), new Point3d(100, 0, 0)),
new Line(new Point3d(100, 0, 0), new Point3d(100, 100, 0))
};
tr.CurrentSpace.AddEntity(entities);
6.6.2 图层表扩展
using var tr = new DBTrans();
// 检查图层是否被使用
tr.LayerTable.CurrentSymbolTable.GenerateUsageData();
var layerRecord = tr.LayerTable.GetRecord("MyLayer");
if (layerRecord != null && !layerRecord.IsUsed)
{
tr.Editor?.WriteMessage("\n图层未被使用,可以安全删除");
}
// 删除图层(带检查的扩展方法)
tr.LayerTable.Remove("UnusedLayer");
6.6.3 从外部导入
using var tr = new DBTrans();
// 从外部文件导入块定义
string sourceFile = "C:\\Library\\blocks.dwg";
ObjectId blockId = tr.BlockTable.GetRecordFrom(
t => t.BlockTable,
sourceFile,
"StandardBlock",
false); // 不覆盖已存在的
// 从外部文件导入图层
ObjectId layerId = tr.LayerTable.GetRecordFrom(
t => t.LayerTable,
sourceFile,
"StandardLayer",
true); // 覆盖已存在的
6.7 几何扩展方法
6.7.1 Point3d扩展
// 点的常用操作
Point3d pt1 = new Point3d(0, 0, 0);
Point3d pt2 = new Point3d(100, 100, 0);
// 计算两点距离
double distance = pt1.DistanceTo(pt2);
// 获取中点
Point3d midPoint = new Point3d(
(pt1.X + pt2.X) / 2,
(pt1.Y + pt2.Y) / 2,
(pt1.Z + pt2.Z) / 2);
// 点的变换
Matrix3d mat = Matrix3d.Displacement(new Vector3d(10, 10, 0));
Point3d transformedPt = pt1.TransformBy(mat);
6.7.2 曲线扩展
using var tr = new DBTrans();
// 创建多段线
var pts = new Point3dCollection
{
new Point3d(0, 0, 0),
new Point3d(100, 0, 0),
new Point3d(100, 100, 0),
new Point3d(0, 100, 0)
};
Polyline pline = new Polyline();
for (int i = 0; i < pts.Count; i++)
{
pline.AddVertexAt(i, new Point2d(pts[i].X, pts[i].Y), 0, 0, 0);
}
pline.Closed = true;
// 获取曲线长度
double length = pline.Length;
// 获取曲线起点和终点
Point3d startPt = pline.StartPoint;
Point3d endPt = pline.EndPoint;
// 获取曲线上的点
Point3d ptAtDist = pline.GetPointAtDist(length / 2);
// 获取曲线在某点的切线方向
Vector3d tangent = pline.GetFirstDerivative(ptAtDist);
tr.CurrentSpace.AddEntity(pline);
6.7.3 区域操作
using var tr = new DBTrans();
// 创建两个圆
var circle1 = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 50);
var circle2 = new Circle(new Point3d(30, 0, 0), Vector3d.ZAxis, 50);
tr.CurrentSpace.AddEntity(circle1);
tr.CurrentSpace.AddEntity(circle2);
// 创建区域
DBObjectCollection curves1 = new DBObjectCollection { circle1 };
DBObjectCollection curves2 = new DBObjectCollection { circle2 };
var regions1 = Region.CreateFromCurves(curves1);
var regions2 = Region.CreateFromCurves(curves2);
if (regions1.Count > 0 && regions2.Count > 0)
{
var region1 = regions1[0] as Region;
var region2 = regions2[0] as Region;
if (region1 != null && region2 != null)
{
// 布尔并集
var unionRegion = region1.Clone() as Region;
unionRegion?.BooleanOperation(BooleanOperationType.BoolUnite, region2);
if (unionRegion != null)
{
tr.CurrentSpace.AddEntity(unionRegion);
}
}
}
6.8 Jig扩展(拖拽预览)
6.8.1 什么是Jig
Jig(动态输入)是CAD中实现拖拽预览的机制。当用户移动鼠标时,Jig可以实时显示图元的预览效果。
6.8.2 简单Jig实现
public class CircleJig : EntityJig
{
private Point3d _center;
private double _radius;
public CircleJig(Point3d center) : base(new Circle())
{
_center = center;
_radius = 0;
Circle circle = (Circle)Entity;
circle.Center = _center;
circle.Radius = 1; // 初始半径
}
protected override SamplerStatus Sampler(JigPrompts prompts)
{
var options = new JigPromptPointOptions("\n指定半径:");
options.BasePoint = _center;
options.UseBasePoint = true;
options.Cursor = CursorType.RubberBand;
var result = prompts.AcquirePoint(options);
if (result.Status == PromptStatus.OK)
{
if (_center.DistanceTo(result.Value) == _radius)
return SamplerStatus.NoChange;
_radius = _center.DistanceTo(result.Value);
return SamplerStatus.OK;
}
return SamplerStatus.Cancel;
}
protected override bool Update()
{
Circle circle = (Circle)Entity;
circle.Radius = _radius > 0 ? _radius : 1;
return true;
}
public double Radius => _radius;
}
// 使用Jig
[CommandMethod("DrawCircleJig")]
public void DrawCircleWithJig()
{
using var tr = new DBTrans();
var ed = tr.Editor;
// 获取圆心
var centerResult = ed?.GetPoint("\n指定圆心:");
if (centerResult?.Status != PromptStatus.OK) return;
// 使用Jig获取半径
var jig = new CircleJig(centerResult.Value);
var result = ed?.Drag(jig);
if (result?.Status == PromptStatus.OK)
{
var circle = new Circle(centerResult.Value, Vector3d.ZAxis, jig.Radius);
tr.CurrentSpace.AddEntity(circle);
ed?.WriteMessage($"\n已绘制圆,半径:{jig.Radius:F2}");
}
}
6.8.3 DrawJig实现
public class MoveJig : DrawJig
{
private Entity[] _entities;
private Point3d _basePoint;
private Point3d _currentPoint;
public MoveJig(Entity[] entities, Point3d basePoint)
{
_entities = entities;
_basePoint = basePoint;
_currentPoint = basePoint;
}
protected override SamplerStatus Sampler(JigPrompts prompts)
{
var options = new JigPromptPointOptions("\n指定目标点:");
options.BasePoint = _basePoint;
options.UseBasePoint = true;
options.Cursor = CursorType.RubberBand;
var result = prompts.AcquirePoint(options);
if (result.Status == PromptStatus.OK)
{
if (result.Value == _currentPoint)
return SamplerStatus.NoChange;
_currentPoint = result.Value;
return SamplerStatus.OK;
}
return SamplerStatus.Cancel;
}
protected override bool WorldDraw(WorldDraw draw)
{
Vector3d displacement = _currentPoint - _basePoint;
Matrix3d mat = Matrix3d.Displacement(displacement);
foreach (var ent in _entities)
{
var clone = ent.Clone() as Entity;
if (clone != null)
{
clone.TransformBy(mat);
draw.Geometry.Draw(clone);
clone.Dispose();
}
}
return true;
}
public Vector3d GetDisplacement()
{
return _currentPoint - _basePoint;
}
}
6.9 实用工具类
6.9.1 进度条
IFoxCAD提供了进度条工具类:
using var tr = new DBTrans();
// 获取所有图元
var result = tr.Editor?.SelectAll();
if (result?.Status != PromptStatus.OK) return;
int count = result.Value.Count;
// 使用进度条
using (var progress = new ProgressMeter())
{
progress.Start("处理图元中...");
progress.SetLimit(count);
int processed = 0;
foreach (SelectedObject obj in result.Value)
{
if (obj == null) continue;
// 处理图元
var ent = tr.GetObject<Entity>(obj.ObjectId, OpenMode.ForWrite);
if (ent != null)
{
// 执行操作...
}
processed++;
progress.MeterProgress();
// 每100个图元更新一次显示
if (processed % 100 == 0)
{
System.Windows.Forms.Application.DoEvents();
}
}
progress.Stop();
}
tr.Editor?.WriteMessage($"\n处理完成,共 {count} 个图元");
6.9.2 随机数生成
using var tr = new DBTrans();
// 创建随机圆
Random rand = new Random();
for (int i = 0; i < 100; i++)
{
double x = rand.NextDouble() * 1000;
double y = rand.NextDouble() * 1000;
double r = rand.NextDouble() * 50 + 10;
var circle = new Circle(new Point3d(x, y, 0), Vector3d.ZAxis, r);
circle.Color = Color.FromColorIndex(ColorMethod.ByAci,
(short)(rand.Next(1, 256)));
tr.CurrentSpace.AddEntity(circle);
}
tr.Editor?.WriteMessage("\n已创建100个随机圆");
6.9.3 系统变量管理
using var tr = new DBTrans();
// 临时修改系统变量
using (var sysVar = new SystemVariableManager())
{
// 关闭对象捕捉
sysVar.Set("OSMODE", 0);
// 关闭命令行回显
sysVar.Set("CMDECHO", 0);
// 执行操作...
// 退出using块时自动恢复原值
}
6.10 本章小结
本章我们深入学习了IFoxCAD的扩展方法体系:
- 扩展方法设计理念:理解了扩展方法的概念和IFoxCAD的组织方式
- Editor扩展:学习了输出、输入、选择等扩展方法
- DBObject扩展:掌握了打开模式、克隆、变换等扩展方法
- ObjectId扩展:学习了有效性检查和快速获取对象的方法
- Collection扩展:掌握了集合类的扩展操作
- 符号表扩展:学习了块表和图层表的扩展方法
- 几何扩展:了解了点、曲线、区域的扩展操作
- Jig扩展:学习了拖拽预览的实现方式
- 实用工具:掌握了进度条、随机数、系统变量等工具
扩展方法是IFoxCAD实现功能扩展的核心机制,熟练掌握这些扩展方法可以大大提高开发效率。下一章我们将学习图元操作与几何处理的高级技巧。

浙公网安备 33010602011771号