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二次开发中使用扩展方法的优势:

  1. 代码简洁:直接在对象上调用,无需额外的工具类
  2. 链式调用:支持流畅的API设计
  3. 类型安全:编译时类型检查
  4. 易于发现: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的扩展方法体系:

  1. 扩展方法设计理念:理解了扩展方法的概念和IFoxCAD的组织方式
  2. Editor扩展:学习了输出、输入、选择等扩展方法
  3. DBObject扩展:掌握了打开模式、克隆、变换等扩展方法
  4. ObjectId扩展:学习了有效性检查和快速获取对象的方法
  5. Collection扩展:掌握了集合类的扩展操作
  6. 符号表扩展:学习了块表和图层表的扩展方法
  7. 几何扩展:了解了点、曲线、区域的扩展操作
  8. Jig扩展:学习了拖拽预览的实现方式
  9. 实用工具:掌握了进度条、随机数、系统变量等工具

扩展方法是IFoxCAD实现功能扩展的核心机制,熟练掌握这些扩展方法可以大大提高开发效率。下一章我们将学习图元操作与几何处理的高级技巧。

posted @ 2025-11-27 12:00  我才是银古  阅读(0)  评论(0)    收藏  举报