第四章:选择集过滤器与图元选择教程

  • Published2026-08-09 12:37:51

第四章:选择集过滤器与图元选择教程

4.1 选择集基础概念

4.1.1 什么是选择集

在AutoCAD中,选择集(Selection Set)是用户选择的一组图元的集合。当用户在CAD中执行命令时,通常需要先选择要操作的对象,这些被选中的对象就组成了选择集。

选择集在CAD二次开发中的作用:

用户交互:让用户选择要处理的图元

批量操作:对多个图元执行相同的操作

数据筛选:从大量图元中筛选出符合条件的对象

4.1.2 CAD原生选择集的使用

使用CAD原生API获取选择集的基本方式:

// 基本选择

Editor ed = doc.Editor;

PromptSelectionResult result = ed.GetSelection();

if (result.Status == PromptStatus.OK)

{

SelectionSet ss = result.Value;

foreach (SelectedObject obj in ss)

{

ObjectId id = obj.ObjectId;

// 处理图元...

}

}

4.1.3 选择集过滤器的重要性

在实际开发中,我们通常不需要选择所有图元,而是需要选择特定类型的图元。这时就需要使用选择集过滤器(Selection Filter)。

原生API的过滤器使用TypedValue数组来定义:

// 原生方式创建过滤器:选择所有直线

TypedValue[] filterList = new TypedValue[]

{

new TypedValue((int)DxfCode.Start, "LINE")

};

SelectionFilter filter = new SelectionFilter(filterList);

PromptSelectionResult result = ed.GetSelection(filter);

这种方式存在的问题:

代码冗长:创建TypedValue数组繁琐

容易出错:DXF代码需要记忆

可读性差:过滤条件不直观

组合困难:复杂条件需要大量代码

4.2 IFoxCAD的OpFilter体系

4.2.1 OpFilter类设计理念

IFoxCAD设计了一套完整的过滤器类体系,以OpFilter为基类,通过运算符重载和链式调用,提供了优雅的过滤器构建方式。

OpFilter的设计目标:

简洁性:用最少的代码表达过滤条件

可读性:代码即文档,条件一目了然

灵活性:支持复杂的逻辑组合

类型安全:编译时检查,减少运行时错误

4.2.2 OpFilter类体系结构

OpFilter (抽象基类)

├── OpEqual - 相等条件

├── OpComp - 比较条件(大于、小于等)

├── OpList - 列表条件(集合基类)

│ ├── OpAnd - 逻辑与

│ ├── OpOr - 逻辑或

│ └── OpXor - 逻辑异或

├── OpLogi - 逻辑条件(非)

│ └── OpNot - 逻辑非

└── Op - 操作符构建类

4.2.3 OpFilter基类

public abstract class OpFilter

{

// 过滤器名称

public abstract string Name { get; }

// 取反操作

public OpFilter Not => new OpNot(this);

// 获取TypedValue序列

public abstract IEnumerable GetValues();

// 非操作符

public static OpFilter operator !(OpFilter item)

{

return item.Not;

}

// 转换为TypedValue数组

public TypedValue[] ToArray()

{

return GetValues().ToArray();

}

// 隐式转换为SelectionFilter

public static implicit operator SelectionFilter(OpFilter item)

{

return new SelectionFilter(item.ToArray());

}

}

关键特性:

抽象设计:定义统一接口,子类实现具体逻辑

隐式转换:可以直接用于需要SelectionFilter的地方

操作符重载:支持!运算符进行取反

4.3 构建过滤器

4.3.1 Build方法

OpFilter提供了Build静态方法,这是构建过滤器最常用的方式:

public static OpFilter Build(Func func)

{

return func(new Op()).Filter!;

}

使用示例:

// 构建过滤器:选择所有直线

var filter = OpFilter.Build(e => e.Dxf(0) == "LINE");

// 使用过滤器

using var tr = new DBTrans();

var result = tr.Editor?.GetSelection(filter);

4.3.2 Dxf方法

Dxf方法用于指定要过滤的DXF组码:

// 按实体类型过滤(组码0)

var filter1 = OpFilter.Build(e => e.Dxf(0) == "LINE");

// 按图层过滤(组码8)

var filter2 = OpFilter.Build(e => e.Dxf(8) == "MyLayer");

// 按颜色过滤(组码62)

var filter3 = OpFilter.Build(e => e.Dxf(62) == 1); // 红色

常用的DXF组码:

组码

说明

示例值

0

实体类型

"LINE", "CIRCLE", "TEXT"

2

块名

"MyBlock"

8

图层名

"0", "MyLayer"

62

颜色索引

1(红), 2(黄), 3(绿)...

6

线型名

"Continuous", "DASHED"

10

起点或中心点

Point3d

11

终点

Point3d

40

半径或高度

double

4.3.3 比较操作符

除了相等判断,还支持各种比较操作:

// 等于

var f1 = OpFilter.Build(e => e.Dxf(0) == "LINE");

// 不等于

var f2 = OpFilter.Build(e => e.Dxf(0) != "CIRCLE");

// 大于(用于数值)

var f3 = OpFilter.Build(e => e.Dxf(40) > 10); // 半径大于10

// 小于

var f4 = OpFilter.Build(e => e.Dxf(40) < 100); // 半径小于100

// 大于等于

var f5 = OpFilter.Build(e => e.Dxf(62) >= 1);

// 小于等于

var f6 = OpFilter.Build(e => e.Dxf(62) <= 255);

4.3.4 点的比较

对于点类型的值,可以进行范围比较:

// 选择起点在某个范围内的直线

var minPt = new Point3d(0, 0, 0);

var maxPt = new Point3d(100, 100, 0);

var filter = OpFilter.Build(e =>

e.Dxf(0) == "LINE" &

e.Dxf(10) >= minPt &

e.Dxf(10) <= maxPt);

4.3.5 通配符匹配

在字符串比较中可以使用通配符:

// 选择所有以"Temp"开头的图层上的图元

var filter = OpFilter.Build(e => e.Dxf(8) == "Temp*");

// 选择块名包含"Door"的块参照

var filter2 = OpFilter.Build(e =>

e.Dxf(0) == "INSERT" & e.Dxf(2) == "*Door*");

通配符说明:

*:匹配任意数量的字符

?:匹配单个字符

#:匹配单个数字

@:匹配单个字母

[...]:匹配括号内的任一字符

[~...]:匹配不在括号内的任一字符

4.4 逻辑组合

4.4.1 逻辑与(And)

使用&运算符或And方法:

// 使用&运算符

var filter1 = OpFilter.Build(e =>

e.Dxf(0) == "LINE" & e.Dxf(8) == "MyLayer");

// 使用And方法

var filter2 = OpFilter.Build(e =>

e.And(

e.Dxf(0) == "LINE",

e.Dxf(8) == "MyLayer",

e.Dxf(62) == 1

));

4.4.2 逻辑或(Or)

使用|运算符或Or方法:

// 使用|运算符

var filter1 = OpFilter.Build(e =>

e.Dxf(0) == "LINE" | e.Dxf(0) == "CIRCLE");

// 使用Or方法

var filter2 = OpFilter.Build(e =>

e.Or(

e.Dxf(0) == "LINE",

e.Dxf(0) == "CIRCLE",

e.Dxf(0) == "ARC"

));

4.4.3 逻辑非(Not)

使用!运算符:

// 选择所有非直线的图元

var filter1 = OpFilter.Build(e => !(e.Dxf(0) == "LINE"));

// 选择不在"0"图层上的图元

var filter2 = OpFilter.Build(e => !(e.Dxf(8) == "0"));

4.4.4 逻辑异或(Xor)

使用^运算符:

// 选择直线或圆,但不同时满足其他条件

var filter = OpFilter.Build(e =>

e.Dxf(0) == "LINE" ^ e.Dxf(0) == "CIRCLE");

4.4.5 复杂条件组合

可以组合多个逻辑操作:

// 复杂条件:选择

// (直线且在Layer1上) 或 (圆且半径大于10) 且不是红色

var filter = OpFilter.Build(e =>

(e.Dxf(0) == "LINE" & e.Dxf(8) == "Layer1" |

e.Dxf(0) == "CIRCLE" & e.Dxf(40) > 10) &

!(e.Dxf(62) == 1));

// 使用方法形式

var filter2 = OpFilter.Build(e =>

e.And(

e.Or(

e.And(e.Dxf(0) == "LINE", e.Dxf(8) == "Layer1"),

e.And(e.Dxf(0) == "CIRCLE", e.Dxf(40) > 10)

),

!(e.Dxf(62) == 1)

));

4.5 获取选择集

4.5.1 GetSelection方法

通过Editor的GetSelection方法获取用户选择:

using var tr = new DBTrans();

// 不带过滤器的选择

var result1 = tr.Editor?.GetSelection();

// 带过滤器的选择

var filter = OpFilter.Build(e => e.Dxf(0) == "LINE");

var result2 = tr.Editor?.GetSelection(filter);

// 处理结果

if (result2?.Status == PromptStatus.OK)

{

SelectionSet ss = result2.Value;

tr.Editor?.WriteMessage($"\n选择了 {ss.Count} 个图元");

}

4.5.2 不同的选择方式

让用户框选或点选

using var tr = new DBTrans();

var filter = OpFilter.Build(e => e.Dxf(0) == "CIRCLE");

var result = tr.Editor?.GetSelection(filter);

if (result?.Status == PromptStatus.OK)

{

foreach (SelectedObject obj in result.Value)

{

var circle = tr.GetObject(obj.ObjectId);

if (circle != null)

{

tr.Editor?.WriteMessage($"\n圆心:{circle.Center}, 半径:{circle.Radius}");

}

}

}

选择全部

using var tr = new DBTrans();

// 选择所有符合条件的图元

var filter = OpFilter.Build(e => e.Dxf(8) == "MyLayer");

var result = tr.Editor?.SelectAll(filter);

if (result?.Status == PromptStatus.OK)

{

tr.Editor?.WriteMessage($"\n共找到 {result.Value.Count} 个图元");

}

窗口选择

using var tr = new DBTrans();

// 定义选择窗口

Point3d pt1 = new Point3d(0, 0, 0);

Point3d pt2 = new Point3d(100, 100, 0);

var filter = OpFilter.Build(e => e.Dxf(0) == "LINE");

// 窗口选择(完全在窗口内的图元)

var result = tr.Editor?.SelectWindow(pt1, pt2, filter);

// 交叉选择(与窗口相交的图元)

var result2 = tr.Editor?.SelectCrossingWindow(pt1, pt2, filter);

围栏选择

using var tr = new DBTrans();

// 定义围栏点

Point3dCollection fence = new Point3dCollection

{

new Point3d(0, 0, 0),

new Point3d(50, 50, 0),

new Point3d(100, 0, 0)

};

var filter = OpFilter.Build(e => e.Dxf(0) == "LINE");

var result = tr.Editor?.SelectFence(fence, filter);

4.5.3 选择提示选项

可以设置选择提示的各种选项:

using var tr = new DBTrans();

// 创建选择选项

var options = new PromptSelectionOptions();

options.MessageForAdding = "\n请选择直线";

options.MessageForRemoval = "\n请移除直线";

options.AllowDuplicates = false;

options.RejectObjectsOnLockedLayers = true;

// 添加关键字

options.Keywords.Add("All", "全选", "全选(A)");

options.Keywords.Add("Cancel", "取消", "取消(C)");

// 处理关键字事件

options.KeywordInput += (s, e) =>

{

if (e.Input == "All")

{

// 处理全选

}

};

var filter = OpFilter.Build(e => e.Dxf(0) == "LINE");

var result = tr.Editor?.GetSelection(options, filter);

4.6 处理选择结果

4.6.1 基本处理

using var tr = new DBTrans();

var filter = OpFilter.Build(e => e.Dxf(0) == "CIRCLE");

var result = tr.Editor?.GetSelection(filter);

if (result?.Status != PromptStatus.OK)

{

tr.Editor?.WriteMessage("\n未选择任何图元");

return;

}

SelectionSet ss = result.Value;

// 遍历选择集

foreach (SelectedObject obj in ss)

{

if (obj == null) continue;

var circle = tr.GetObject(obj.ObjectId, OpenMode.ForWrite);

if (circle != null)

{

// 修改圆的颜色

circle.Color = Color.FromColorIndex(ColorMethod.ByAci, 1);

}

}

tr.Editor?.WriteMessage($"\n已修改 {ss.Count} 个圆的颜色");

4.6.2 使用LINQ处理

using var tr = new DBTrans();

var filter = OpFilter.Build(e => e.Dxf(0) == "CIRCLE");

var result = tr.Editor?.GetSelection(filter);

if (result?.Status != PromptStatus.OK) return;

// 使用LINQ获取所有圆

var circles = result.Value

.Cast()

.Where(obj => obj != null)

.Select(obj => tr.GetObject(obj.ObjectId))

.Where(c => c != null);

// 统计信息

double totalArea = circles.Sum(c => Math.PI * c!.Radius * c.Radius);

double avgRadius = circles.Average(c => c!.Radius);

tr.Editor?.WriteMessage($"\n总面积:{totalArea:F2}");

tr.Editor?.WriteMessage($"\n平均半径:{avgRadius:F2}");

4.6.3 使用扩展方法

IFoxCAD提供了选择集的扩展方法:

using var tr = new DBTrans();

var filter = OpFilter.Build(e => e.Dxf(0) == "LINE");

var result = tr.Editor?.GetSelection(filter);

if (result?.Status != PromptStatus.OK) return;

// 获取所有ObjectId

var ids = result.Value.GetObjectIds();

// 获取所有图元

var entities = ids.Select(id => tr.GetObject(id)).Where(e => e != null);

// 批量修改

foreach (var ent in entities)

{

using (ent!.ForWrite())

{

ent.Layer = "NewLayer";

}

}

4.7 高级过滤技巧

4.7.1 扩展数据过滤

可以根据扩展数据(XData)进行过滤:

using var tr = new DBTrans();

// 过滤具有特定应用程序扩展数据的图元

// 组码-3表示扩展数据

var filter = OpFilter.Build(e =>

e.Dxf(-3) == "MyAppName");

var result = tr.Editor?.GetSelection(filter);

4.7.2 块参照内容过滤

过滤特定块的块参照:

using var tr = new DBTrans();

// 选择特定块的所有参照

var filter = OpFilter.Build(e =>

e.Dxf(0) == "INSERT" & e.Dxf(2) == "MyBlock");

var result = tr.Editor?.GetSelection(filter);

4.7.3 多类型过滤

同时选择多种类型的图元:

using var tr = new DBTrans();

// 选择直线、圆和圆弧

var filter = OpFilter.Build(e =>

e.Or(

e.Dxf(0) == "LINE",

e.Dxf(0) == "CIRCLE",

e.Dxf(0) == "ARC"

));

var result = tr.Editor?.GetSelection(filter);

4.7.4 嵌套条件

复杂的嵌套条件:

using var tr = new DBTrans();

// (在Layer1上的直线或圆) 或 (在Layer2上且为红色的任何图元)

var filter = OpFilter.Build(e =>

e.Or(

e.And(

e.Dxf(8) == "Layer1",

e.Or(e.Dxf(0) == "LINE", e.Dxf(0) == "CIRCLE")

),

e.And(

e.Dxf(8) == "Layer2",

e.Dxf(62) == 1

)

));

var result = tr.Editor?.GetSelection(filter);

4.8 常用过滤器示例

4.8.1 按图元类型选择

public static class FilterExamples

{

// 选择所有直线

public static OpFilter Lines() =>

OpFilter.Build(e => e.Dxf(0) == "LINE");

// 选择所有圆

public static OpFilter Circles() =>

OpFilter.Build(e => e.Dxf(0) == "CIRCLE");

// 选择所有圆弧

public static OpFilter Arcs() =>

OpFilter.Build(e => e.Dxf(0) == "ARC");

// 选择所有多段线

public static OpFilter Polylines() =>

OpFilter.Build(e =>

e.Dxf(0) == "LWPOLYLINE" | e.Dxf(0) == "POLYLINE");

// 选择所有文字

public static OpFilter Texts() =>

OpFilter.Build(e =>

e.Dxf(0) == "TEXT" | e.Dxf(0) == "MTEXT");

// 选择所有块参照

public static OpFilter BlockReferences() =>

OpFilter.Build(e => e.Dxf(0) == "INSERT");

// 选择所有标注

public static OpFilter Dimensions() =>

OpFilter.Build(e => e.Dxf(0) == "DIMENSION");

// 选择所有填充

public static OpFilter Hatches() =>

OpFilter.Build(e => e.Dxf(0) == "HATCH");

}

4.8.2 按属性选择

public static class AttributeFilters

{

// 按图层选择

public static OpFilter ByLayer(string layerName) =>

OpFilter.Build(e => e.Dxf(8) == layerName);

// 按颜色选择

public static OpFilter ByColor(int colorIndex) =>

OpFilter.Build(e => e.Dxf(62) == colorIndex);

// 按线型选择

public static OpFilter ByLinetype(string linetypeName) =>

OpFilter.Build(e => e.Dxf(6) == linetypeName);

// 选择特定块的参照

public static OpFilter ByBlockName(string blockName) =>

OpFilter.Build(e =>

e.Dxf(0) == "INSERT" & e.Dxf(2) == blockName);

// 选择半径大于指定值的圆

public static OpFilter CirclesWithRadiusGreaterThan(double radius) =>

OpFilter.Build(e =>

e.Dxf(0) == "CIRCLE" & e.Dxf(40) > radius);

}

4.8.3 组合过滤器

public static class CombinedFilters

{

// 选择指定图层上的指定类型图元

public static OpFilter TypeOnLayer(string entityType, string layerName) =>

OpFilter.Build(e =>

e.Dxf(0) == entityType & e.Dxf(8) == layerName);

// 选择多个图层上的图元

public static OpFilter OnLayers(params string[] layerNames)

{

if (layerNames.Length == 0)

return OpFilter.Build(e => e.Dxf(0) == "*");

if (layerNames.Length == 1)

return OpFilter.Build(e => e.Dxf(8) == layerNames[0]);

return OpFilter.Build(e =>

{

var first = e.Dxf(8) == layerNames[0];

foreach (var name in layerNames.Skip(1))

{

first = first | e.Dxf(8) == name;

}

return first;

});

}

// 选择除了指定图层外的所有图元

public static OpFilter NotOnLayer(string layerName) =>

OpFilter.Build(e => !(e.Dxf(8) == layerName));

}

4.9 实际应用案例

4.9.1 图元统计工具

[CommandMethod("EntityStats")]

public void EntityStatistics()

{

using var tr = new DBTrans();

// 获取所有图元

var result = tr.Editor?.SelectAll();

if (result?.Status != PromptStatus.OK) return;

// 统计各类型图元数量

var stats = result.Value

.Cast()

.Where(obj => obj != null)

.Select(obj => tr.GetObject(obj.ObjectId))

.Where(ent => ent != null)

.GroupBy(ent => ent!.GetType().Name)

.Select(g => new { Type = g.Key, Count = g.Count() })

.OrderByDescending(x => x.Count);

tr.Editor?.WriteMessage("\n\n图元统计:");

tr.Editor?.WriteMessage("\n" + new string('-', 30));

foreach (var stat in stats)

{

tr.Editor?.WriteMessage($"\n{stat.Type,-20}{stat.Count,10}");

}

tr.Editor?.WriteMessage("\n" + new string('-', 30));

tr.Editor?.WriteMessage($"\n{"总计",-20}{result.Value.Count,10}");

}

4.9.2 批量修改图层

[CommandMethod("BatchChangeLayer")]

public void BatchChangeLayer()

{

using var tr = new DBTrans();

// 获取源图层名

var sourceResult = tr.Editor?.GetString("\n请输入源图层名:");

if (sourceResult?.Status != PromptStatus.OK) return;

string sourceLayer = sourceResult.StringResult;

// 获取目标图层名

var targetResult = tr.Editor?.GetString("\n请输入目标图层名:");

if (targetResult?.Status != PromptStatus.OK) return;

string targetLayer = targetResult.StringResult;

// 确保目标图层存在

if (!tr.LayerTable.Has(targetLayer))

{

tr.LayerTable.Add(targetLayer);

}

// 选择源图层上的所有图元

var filter = OpFilter.Build(e => e.Dxf(8) == sourceLayer);

var selectResult = tr.Editor?.SelectAll(filter);

if (selectResult?.Status != PromptStatus.OK)

{

tr.Editor?.WriteMessage($"\n图层 {sourceLayer} 上没有图元");

return;

}

// 修改图层

int count = 0;

foreach (SelectedObject obj in selectResult.Value)

{

if (obj == null) continue;

var ent = tr.GetObject(obj.ObjectId, OpenMode.ForWrite);

if (ent != null)

{

ent.Layer = targetLayer;

count++;

}

}

tr.Editor?.WriteMessage($"\n已将 {count} 个图元从 {sourceLayer} 移动到 {targetLayer}");

}

4.9.3 圆形标注工具

[CommandMethod("AnnotateCircles")]

public void AnnotateCircles()

{

using var tr = new DBTrans();

// 选择圆

var filter = OpFilter.Build(e => e.Dxf(0) == "CIRCLE");

var result = tr.Editor?.GetSelection(filter);

if (result?.Status != PromptStatus.OK)

{

tr.Editor?.WriteMessage("\n未选择任何圆");

return;

}

// 确保标注图层存在

if (!tr.LayerTable.Has("标注"))

{

tr.LayerTable.Add("标注", layer =>

{

layer.Color = Color.FromColorIndex(ColorMethod.ByAci, 3);

});

}

// 为每个圆添加标注

foreach (SelectedObject obj in result.Value)

{

if (obj == null) continue;

var circle = tr.GetObject(obj.ObjectId);

if (circle == null) continue;

// 创建标注文字

var text = new DBText();

text.Position = circle.Center;

text.TextString = $"R={circle.Radius:F2}";

text.Height = circle.Radius / 3;

text.Layer = "标注";

text.HorizontalMode = TextHorizontalMode.TextCenter;

text.VerticalMode = TextVerticalMode.TextVerticalMid;

text.AlignmentPoint = circle.Center;

tr.CurrentSpace.AddEntity(text);

}

tr.Editor?.WriteMessage($"\n已为 {result.Value.Count} 个圆添加标注");

}

4.9.4 图元高亮显示

[CommandMethod("HighlightByType")]

public void HighlightByType()

{

using var tr = new DBTrans();

// 获取要高亮的类型

var typeResult = tr.Editor?.GetString("\n请输入图元类型(如LINE、CIRCLE):");

if (typeResult?.Status != PromptStatus.OK) return;

string entityType = typeResult.StringResult.ToUpper();

// 选择指定类型的图元

var filter = OpFilter.Build(e => e.Dxf(0) == entityType);

var result = tr.Editor?.SelectAll(filter);

if (result?.Status != PromptStatus.OK)

{

tr.Editor?.WriteMessage($"\n未找到类型为 {entityType} 的图元");

return;

}

// 高亮显示

foreach (SelectedObject obj in result.Value)

{

if (obj == null) continue;

var ent = tr.GetObject(obj.ObjectId);

if (ent != null)

{

ent.Highlight();

}

}

tr.Editor?.WriteMessage($"\n已高亮 {result.Value.Count} 个 {entityType} 图元");

tr.Editor?.WriteMessage("\n按任意键取消高亮...");

tr.Editor?.GetString("");

// 取消高亮

foreach (SelectedObject obj in result.Value)

{

if (obj == null) continue;

var ent = tr.GetObject(obj.ObjectId);

if (ent != null)

{

ent.Unhighlight();

}

}

}

4.10 本章小结

本章我们深入学习了IFoxCAD的选择集过滤器系统:

选择集基础:理解了选择集的概念和CAD原生过滤器的使用问题

OpFilter体系:学习了OpFilter类的设计和体系结构

构建过滤器:掌握了Build方法、Dxf方法和各种比较操作

逻辑组合:学习了And、Or、Not等逻辑操作的使用

获取选择集:掌握了各种选择方式和选项设置

处理结果:学习了选择结果的处理方法

高级技巧:了解了扩展数据过滤、嵌套条件等高级用法

实际案例:通过多个实际案例巩固所学知识

OpFilter系统极大地简化了选择集过滤器的创建,使代码更加简洁和可读。下一章我们将学习扩展数据与字典操作,这是CAD二次开发中数据存储的重要方式。

← 上一章

目录

下一章 →