04-选择集过滤器与图元选择教程
第四章:选择集过滤器与图元选择教程
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<TypedValue> 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<Op, Op> 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<Circle>(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<Circle>(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<SelectedObject>()
.Where(obj => obj != null)
.Select(obj => tr.GetObject<Circle>(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<Entity>(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<SelectedObject>()
.Where(obj => obj != null)
.Select(obj => tr.GetObject<Entity>(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<Entity>(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<Circle>(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<Entity>(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<Entity>(obj.ObjectId);
if (ent != null)
{
ent.Unhighlight();
}
}
}
4.10 本章小结
本章我们深入学习了IFoxCAD的选择集过滤器系统:
- 选择集基础:理解了选择集的概念和CAD原生过滤器的使用问题
- OpFilter体系:学习了OpFilter类的设计和体系结构
- 构建过滤器:掌握了Build方法、Dxf方法和各种比较操作
- 逻辑组合:学习了And、Or、Not等逻辑操作的使用
- 获取选择集:掌握了各种选择方式和选项设置
- 处理结果:学习了选择结果的处理方法
- 高级技巧:了解了扩展数据过滤、嵌套条件等高级用法
- 实际案例:通过多个实际案例巩固所学知识
OpFilter系统极大地简化了选择集过滤器的创建,使代码更加简洁和可读。下一章我们将学习扩展数据与字典操作,这是CAD二次开发中数据存储的重要方式。

浙公网安备 33010602011771号