蚂蚁线AntLine 控件调用文档
命名空间:CConUI
继承:System.Windows.Forms.Control
定位:虚线蚂蚁线选区控件,支持鼠标穿透、8 向拉伸、整体移动、滚轮缩放、边界限制、固定大小模式,以及与父控件(如 PictureBox)联动。
依赖:CConPlus.Enum.RectOffset(struct,含 Left / Top / Right / Bottom 四个 int 字段)
本控件使用 using static CConPlus.Enum;,因此属性签名里出现 RectOffset 时实际类型为 CConPlus.Enum.RectOffset。
一、作用
常用于图片裁剪、截图工具、屏幕取色、局部放大等场景,在父控件上叠加一层“虚线选框”,具备如下特性:
鼠标穿透:未命中选框时鼠标消息穿透到父控件,不干扰父控件(如图片拖动)
8 向拉伸:N / S / W / E / NW / NE / SW / SE 八个方向拉伸缩放
整体移动:命中内部拖动即可移动
新建选框:在空白处按住
Ctrl+ 左键拖动即可创建新选框滚轮缩放:以鼠标位置为中心等比缩放(最小 4×4)
边界限制:默认限制在父控件客户区内,可用
RestrictRect向内偏移固定大小模式:为
true时不能拉伸、碰边只平移不裁剪蚂蚁线动画:虚线持续滚动
二、快速上手
using CConUI;
// 1. 创建蚂蚁线控件
AntLine ant = new AntLine();
// 2. 添加到父容器(通常是 PictureBox 或 Panel),并 Dock 填充
ant.Parent = pictureBox1;
ant.Dock = DockStyle.Fill;
// 3. 设置选框矩形(坐标相对 AntLine 客户区,即父控件客户区)
ant.AntRect = new Rectangle(50, 50, 200, 150);
// 4. 显示(默认为 true,但如果之前被 Hide 过需要恢复)
ant.Visible = true;
// 5. 监听矩形变化
ant.AntRectChanged += (s, e) =>
{
Console.WriteLine("矩形变化:" + ant.AntRect);
};坐标说明:
AntRect使用的是 AntLine 控件客户区坐标系。由于 AntLine 通常以Dock = Fill铺满父控件(如 PictureBox),因此其客户区坐标与父控件客户区坐标重合——这也是它被称为“贴合父控件选框”的原因。
三、构造函数
public AntLine()
| 项目 | 说明 |
|---|---|
| 参数 | 无 |
| 作用 | 初始化蚂蚁线控件 |
| 初始样式 | SupportsTransparentBackColor | UserPaint | AllPaintingInWmPaint | OptimizedDoubleBuffer,BackColor = Transparent |
| 焦点 | Selectable = false,TabStop = false(不抢占父控件键盘/滚轮事件) |
| 定时器 | 创建并启动动画 Timer,间隔 AnimationInterval = 100,每次让虚线偏移 +1 像素 |
| 初始矩形 | Rectangle.Empty(不显示,需自行赋值 AntRect) |
| 初始外观 | Color1 = White、Color2 = Black、DashLength = 4、DashGap = 4、AntLineSize = 1 |
| 初始行为 | WheelZoomEnabled = true、RestrictRect = null、FixedSize = false |
var ant = new AntLine
{
Parent = pictureBox1,
Dock = DockStyle.Fill,
AntRect = new Rectangle(100, 100, 300, 200)
};四、公开属性
4.1 public int AnimationInterval
| 项目 | 说明 |
|---|---|
| 类型 | int(毫秒) |
| 默认 | 100(源码字段 _antSpeed = 100) |
| Category | CCon_外观 |
| 作用 | 蚂蚁线动画刷新间隔。数值越小,虚线滚动越快 |
| 取值范围 | 仅当 value > 10 时才被接受(≤10 会被忽略) |
| 副作用 | 若定时器已创建,会立即更新其 Interval,无需重启 |
⚠ 属性描述文本写的是“默认50”,但源码中字段初值为
100。请以实际代码为准。
ant.AnimationInterval = 50; // 更快的虚线动画 ant.AnimationInterval = 200; // 更慢的虚线动画
4.2 public Rectangle AntRect
| 项目 | 说明 |
|---|---|
| 类型 | Rectangle |
| 默认 | Rectangle.Empty(不显示) |
| Category | (未标注,即设计器分类默认 杂项) |
| 坐标 | AntLine 控件客户区坐标系(通常等同父控件客户区) |
| 作用 | 蚂蚁线的矩形区域。设置时会自动应用边界限制(见 RestrictRect / FixedSize) |
| 副作用 | ① 如果值与旧值不同:Invalidate() 重绘;② 触发 AntRectChanged;③ 若宽或高变化 → 触发 AntRectResized;④ 否则若坐标变化 → 触发 AntRectMoved |
| 设空 | 传入 Rectangle.Empty 时不做 ApplyRestriction,直接生效(用于隐藏选框) |
ant.AntRect = new Rectangle(50, 50, 200, 150); // 显示选框 ant.AntRect = Rectangle.Empty; // 隐藏选框(不绘制)
4.3 public Color Color1
| 项目 | 说明 |
|---|---|
| 类型 | Color |
| 默认 | Color.White |
| Category | CCon_外观 |
| 编辑器 | ArgbColorEditor(支持 ARGB 可视化选择) |
| 作用 | 蚂蚁线的主色(第一组虚线颜色) |
ant.Color1 = Color.Lime;
4.4 public Color Color2
| 项目 | 说明 |
|---|---|
| 类型 | Color |
| 默认 | Color.Black |
| Category | CCon_外观 |
| 编辑器 | ArgbColorEditor |
| 作用 | 蚂蚁线的副色(第二组虚线颜色),与主色交错形成经典“蚂蚁线”效果 |
ant.Color1 = Color.White; ant.Color2 = Color.Black; // 经典黑白蚂蚁线
4.5 public int DashLength
| 项目 | 说明 |
|---|---|
| 类型 | int(像素) |
| 默认 | 4 |
| Category | CCon_外观 |
| 作用 | 虚线实线段的长度(像素) |
| 取值范围 | 仅当 value > 0 时才被接受 |
ant.DashLength = 6;
4.6 public int DashGap
| 项目 | 说明 |
|---|---|
| 类型 | int(像素) |
| 默认 | 4 |
| Category | CCon_外观 |
| 作用 | 虚线间隔段的长度(像素) |
| 取值范围 | 仅当 value > 0 时才被接受 |
ant.DashGap = 6;
4.7 public int AntLineSize
| 项目 | 说明 |
|---|---|
| 类型 | int(像素) |
| 默认 | 1 |
| Category | CCon_外观 |
| 作用 | 线条粗细;同时影响边缘命中容差(_hitTolerance + AntLineSize / 2) |
| 取值范围 | 仅当 value > 0 时才被接受 |
ant.AntLineSize = 2;
4.8 public bool WheelZoomEnabled
| 项目 | 说明 |
|---|---|
| 类型 | bool |
| 默认 | true |
| Category | CCon_控制 |
| 作用 | 是否允许鼠标滚轮缩放选框大小。仅在鼠标命中选框(内部或边缘容差内)时才生效 |
ant.WheelZoomEnabled = false; // 禁用滚轮缩放
4.9 public RectOffset? RestrictRect
| 项目 | 说明 |
|---|---|
| 类型 | CConPlus.Enum.RectOffset?(可空 struct) |
| 默认 | null(限制区域 = 父控件客户区) |
| Category | CCon_控制 |
| 作用 | 在父控件客户区基础上向内偏移,作为选框的合法区域。 例如 new RectOffset(10, 10, 10, 10) 表示四面各向内 10 像素 |
| 副作用 | 若当前已有非空 AntRect,赋值后会立即执行一次 AntRect = ApplyRestriction(AntRect) 强制回到新边界内 |
| 特殊场景 | 若偏移量过大,导致有效区域为空(Width ≤ 0 或 Height ≤ 0),ApplyRestriction 返回 Rectangle.Empty,选框被清空 |
using CConPlus.Enum; // 在父控件四周各向内偏移 10 像素作为可移动区域 ant.RestrictRect = new RectOffset(10, 10, 10, 10); // 取消限制 ant.RestrictRect = null;
RectOffset是 struct,字段为Left、Top、Right、Bottom(顺序按源码new Rectangle(parent.X + L, parent.Y + T, W - L - R, H - T - B)的解释)。
4.10 public bool FixedSize
| 项目 | 说明 |
|---|---|
| 类型 | bool |
| 默认 | false |
| Category | CCon_控制 |
| 作用 | 是否固定选框大小。true:① 边缘命中被强制降级为 Move(无法拉伸);② 边界碰撞时只平移,不裁剪宽高;false:正常 8 向拉伸;碰边裁剪缩小 |
| 副作用 | 赋值后立即 Invalidate() |
ant.FixedSize = true; // 锁定大小,只能移动
五、公开事件
5.1 public event EventHandler AntRectChanged
| 项目 | 说明 |
|---|---|
| 参数 | EventHandler(e 为 EventArgs.Empty) |
| 触发时机 | AntRect 值发生实际变化时最先触发(无论移动还是缩放) |
| 触发顺序 | 先 AntRectChanged → 再视情况触发 AntRectResized 或 AntRectMoved |
ant.AntRectChanged += (s, e) =>
{
// 获取当前选框
Rectangle r = ((AntLine)s).AntRect;
label1.Text = $"选区:{r.X},{r.Y} 尺寸 {r.Width}×{r.Height}";
};5.2 public event EventHandler AntRectResized
| 项目 | 说明 |
|---|---|
| Category | CCon_事件 |
| 触发时机 | 当 AntRect 的 Width 或 Height 发生变化时触发(包括:边缘拉伸、滚轮缩放、 RestrictRect 导致裁剪、外部赋值改变大小) |
| 不触发情况 | 只是移动位置(宽高不变)时不触发 |
ant.AntRectResized += (s, e) =>
{
Rectangle r = ((AntLine)s).AntRect;
Console.WriteLine("尺寸变化 → " + r.Width + " × " + r.Height);
};5.3 public event EventHandler AntRectMoved
| 项目 | 说明 |
|---|---|
| Category | CCon_事件 |
| 触发时机 | 当 AntRect 的宽高不变,但 X 或 Y 发生变化时触发(包括:整体拖动、外部赋值改坐标、 FixedSize 碰边平移) |
| 不触发情况 | 宽高同时改变时(此时走 AntRectResized)不触发 |
ant.AntRectMoved += (s, e) =>
{
Rectangle r = ((AntLine)s).AntRect;
Console.WriteLine("位置变化 → " + r.X + "," + r.Y);
};事件互斥逻辑(按源码):
if (宽或高变了) 触发 AntRectResized; else if (X 或 Y 变了) 触发 AntRectMoved;因此一次赋值只会触发两者之一,而
AntRectChanged每次都会触发(只要值变化)。若同时改变位置和大小,只会触发AntRectResized。
六、公开方法
本控件没有自定义 public 方法。
其功能全部通过属性赋值(AntRect)和鼠标/键盘交互完成。
继承的公开方法(如Invalidate、Focus、BringToFront等)来自Control,可按需使用。
七、鼠标交互一览
| 操作 | 条件 | 效果 |
|---|---|---|
| 鼠标悬停内部 | 选框内 | 光标变为 SizeAll,表示可拖动 |
| 悬停边缘/角 | 选框边缘或角容差内 | 光标变为对应拉伸样式(SizeNS / SizeWE / SizeNWSE / SizeNESW) |
| 悬停空白处 + 按 Ctrl | 选框外 | 光标变为 Cross,提示可新建 |
| 左键按下内部 | 命中内部 | 进入 Move 拖动模式,捕获鼠标 |
| 左键按边缘/角 | 命中边缘/角 | 进入对应方向的拉伸模式 |
| 左键空白处 + Ctrl | 选框外 + Ctrl | 进入 Create 新建模式,选框从无到有 |
| 拖动过程中 | 拖动/拉伸/新建 | 实时更新 AntRect,自动应用边界限制 |
| 左键抬起 | 拖动状态中 | 结束拖动、释放捕获 |
| 滚轮向上(Delta>0) | WheelZoomEnabled=true 且命中选框 | 以鼠标位置为中心放大 1.1 倍(最小 4×4) |
| 滚轮向下(Delta<0) | 同上 | 缩小 1/1.1 倍 |
| 鼠标未命中选框 | (WndProc 拦截 WM_NCHITTEST) | 返回 HTTRANSPARENT,鼠标消息穿透到父控件 |
边缘命中容差:tol = _hitTolerance(6) + AntLineSize / 2,即在选框边缘外 tol 像素内都算命中边缘。
八、边界限制逻辑
| 模式 | 属性 | 逻辑 |
|---|---|---|
| 默认边界 | RestrictRect = null | 允许区域 = Parent.ClientRectangle |
| 自定义边距 | RestrictRect = RectOffset(L, T, R, B) | 允许区域 = Parent.ClientRectangle 向内偏移 L/T/R/B 像素 |
| 普通模式裁剪 | FixedSize = false | 调用 Rectangle.Intersect(rect, bounds),超出边界的部分会被直接裁剪(宽高变小) |
| 固定大小平移 | FixedSize = true | 碰边时只平移 X/Y 使其回到边界内,宽高保持不变;只有选框比边界还大时才强制截断 |
// 限制到父控件四周向内 10 像素区域,且框大小锁定 ant.RestrictRect = new RectOffset(10, 10, 10, 10); ant.FixedSize = true; ant.AntRect = new Rectangle(50, 50, 200, 150); // 若碰边则自动平移,不变形
九、蚂蚁线绘制细节
| 项目 | 说明 |
|---|---|
| 主色/副色 | Color1 与 Color2 两遍绘制,形成双色交错虚线 |
| 虚线段 | DashPattern = {DashLength / AntLineSize, DashGap / AntLineSize}(因 GDI+ 相对单位是线宽倍数) |
| 相位交错 | Color2 的 DashOffset 比 Color1 多一个 DashLength / AntLineSize,实现无缝交错 |
| 动画 | 每 AnimationInterval 毫秒让 _dashOffset +1 像素(取模),触发 Invalidate(_antRect) 局部重绘 |
| 防裁剪 | PenAlignment.Inset 且绘制矩形宽高 −1,防止线宽较大时右侧/下侧被裁切 |
| 空矩形 | AntRect == Rectangle.Empty 时 OnPaint 直接返回,定时器也不重绘 |
十、鼠标穿透机制
本控件重写了
WndProc,处理 Windows 消息WM_NCHITTEST (0x0084):
取鼠标屏幕坐标转成控件客户区坐标;
若
HitTest(p) == HitArea.None(未命中内部、边缘或容差区域),返回HTTRANSPARENT (-1),让父控件接收该鼠标消息;否则正常返回,由本控件处理。
因此在
PictureBox(MouseEnabled = true)上叠加AntLine后:
鼠标在选框内部或边缘时,滚轮/拖动被 AntLine 消费(拖选框);
鼠标在选框外的空白处时,滚轮/拖动会穿透到 PictureBox(缩放/拖动图片)。
十一、完整使用示例
示例 1:最简选框
var ant = new AntLine
{
Parent = pictureBox1,
Dock = DockStyle.Fill,
AntRect = new Rectangle(50, 50, 200, 150)
};
ant.AntRectChanged += (s, e) => Console.WriteLine(ant.AntRect);示例 2:与 PictureBox 联动裁剪
// 假设已经有:
// PictureBox pb(MouseEnabled = true)
// AntLine ant(Dock=Fill 到 pb)
ant.AntRect = new Rectangle(100, 100, 300, 200);
// 通过按钮触发裁剪
btnCrop.Click += (s, e) =>
{
using (Bitmap bmp = pb.GetImageInRectangle(ant.AntRect))
{
if (bmp != null)
{
bmp.Save("cropped.png", System.Drawing.Imaging.ImageFormat.Png);
MessageBox.Show("已保存 cropped.png");
}
}
};由于
AntLine以Dock=Fill填充PictureBox,AntRect使用的坐标与PictureBox客户区坐标一致,可以直接传给pb.GetImageInRectangle(...)。
示例 3:固定大小 + 边距约束
using CConPlus.Enum;
var ant = new AntLine
{
Parent = pictureBox1,
Dock = DockStyle.Fill,
FixedSize = true, // 锁定大小,只能移动
RestrictRect = new RectOffset(20, 20, 20, 20), // 距离父控件四边各 20 像素
AntRect = new Rectangle(100, 100, 200, 150),
Color1 = Color.Lime,
Color2 = Color.Black,
DashLength = 6,
DashGap = 4,
AntLineSize = 2
};示例 4:关闭滚轮缩放(不与父控件争抢滚轮)
var ant = new AntLine
{
Parent = pictureBox1,
Dock = DockStyle.Fill,
WheelZoomEnabled = false, // 不使用滚轮缩放
AntRect = new Rectangle(50, 50, 200, 150)
};示例 5:用事件分别处理移动与缩放
ant.AntRectMoved += (s, e) =>
{
Rectangle r = ((AntLine)s).AntRect;
lblPos.Text = $"位置:({r.X}, {r.Y})";
};
ant.AntRectResized += (s, e) =>
{
Rectangle r = ((AntLine)s).AntRect;
lblSize.Text = $"尺寸:{r.Width} × {r.Height}";
};示例 6:程序化创建/清空选框
// 显示一个从 (100,100) 到 (300,250) 的选框 ant.AntRect = new Rectangle(100, 100, 200, 150); // 清空(隐藏选框、停止绘制) ant.AntRect = Rectangle.Empty;
十二、公开成员速查表
| 成员 | 类型 | 说明 |
|---|---|---|
AntLine() | 构造函数 | 创建控件并启动动画定时器 |
AntRect | 属性 | 选框矩形;赋值自动应用边界限制并触发事件 |
Color1 | 属性(ARGB) | 主色(默认白) |
Color2 | 属性(ARGB) | 副色(默认黑) |
DashLength | 属性 | 实线长度(像素) |
DashGap | 属性 | 间隔长度(像素) |
AntLineSize | 属性 | 线条粗细(像素) |
AnimationInterval | 属性 | 动画刷新间隔(毫秒),>10 生效 |
WheelZoomEnabled | 属性 | 是否允许滚轮缩放 |
RestrictRect | 属性(可空 struct) | 边界向内偏移 |
FixedSize | 属性 | 锁定大小(禁止拉伸,碰边只平移) |
AntRectChanged | 事件 | 矩形任意变化时触发 |
AntRectResized | 事件 | 宽高变化时触发 |
AntRectMoved | 事件 | 位置变化(宽高不变)时触发 |
十三、注意事项
坐标是 AntLine 客户区坐标。AntLine 应
Dock = Fill在父控件(如 PictureBox)上,此时 AntRect 坐标 = 父控件客户区坐标。默认不显示。构造时
AntRect = Rectangle.Empty,必须显式赋值才会绘制。可设置ant.AntRect = Rectangle.Empty来隐藏。事件互斥:
AntRectResized和AntRectMoved不会同时触发。同时改位置和大小时只触发AntRectResized。若需要“任意变化”用AntRectChanged。赋相同值不触发事件。
AntRect的 setter 会先对比_antRect != newRect,相同则直接返回,避免重复重绘和事件。范围限制:
AnimationInterval必须 >10;DashLength/DashGap/AntLineSize必须 >0,否则被忽略。滚轮缩放最小尺寸为 4×4,无法再缩小。
滚轮缩放只对命中的选框生效。要禁用滚轮交互(让滚轮完全穿透到父控件)请设
WheelZoomEnabled = false;即使true,未命中选框时 WndProc 也会返回HTTRANSPARENT,让父控件处理。固定大小模式无法拉伸:
FixedSize=true时,命中边缘也会被当成Move,无法拉伸。但滚轮缩放仍有效(代码里OnMouseWheel未检查FixedSize,只用了ApplyFixedSizeRestriction,因此滚轮仍能改变大小,但会保持“碰边平移”的语义)。如需完全锁定,需额外设置WheelZoomEnabled=false。RectOffset 是 struct,可空。设
null表示只受父控件客户区限制;设值表示在父控件基础上再向内偏移。偏移量过大导致有效区域为空时,
ApplyRestriction返回Rectangle.Empty,选框会被清空。构造时立即启动定时器。即使
AntRect = Empty,定时器仍每AnimationInterval触发一次(只是不重绘)。对性能敏感的场景可考虑在隐藏时不创建控件(Controls.Remove),或在Dispose时自动停止(代码已处理)。AntLine不抢占焦点(Selectable = false、TabStop = false),不会影响父控件的键盘和滚轮事件处理。AntRectChanged / AntRectResized / AntRectMoved在内部鼠标拖动、滚轮缩放、外部赋值时都会触发,不需要额外订阅鼠标事件即可得知矩形变化。与
PictureBox配合时,由于AntLine在PictureBox.Controls中,作为子控件会随PictureBox的图片拖动而移动吗?不会——AntLine的Parent是PictureBox,Dock=Fill 是相对父控件的客户端区域固定的。图片的 zoom/offset 只影响PictureBox自身OnPaint的绘制,不影响子控件位置。
因此AntRect的坐标是“**屏幕空间(父控件可视空间)**”,配合PictureBox.GetImageInRectangle(antRect)时,控件内部会自动完成反变换,得到当前视口该区域显示的原图内容。
转载请注明出处。