IMouseOverShape

张开发
2026/5/3 11:46:30 15 分钟阅读
IMouseOverShape
IMouseOverShape 和 MouseOverShapeBase 详解 - 鼠标悬停形状这是形状系统的鼠标悬停功能实现让形状能够检测鼠标是否经过并以特殊样式显示反馈是交互式标注工具的基础功能。 文件头部和命名空间// 版权信息同前// Copyright (c) HeBianGu Authors. All Rights Reserved.// ...省略namespaceH.LabelImg.ShapeBox.Shapes.Base;// 命名空间存放形状的基类 IMouseOverShape 接口// 接口定义表示一个可以对鼠标悬停做出反应的形状// 继承自 IHitableShape具有被击中检测能力publicinterfaceIMouseOverShape:IHitableShape{// 绘制鼠标悬停状态下的形状// 参数说明// view - 视图对象提供缩放等信息// drawingContext - WPF绘图上下文// stroke - 悬停时的边框颜色// strokeThickness - 悬停时的边框粗细// fill - 悬停时的填充颜色voidDrawMouseOver(IViewview,DrawingContextdrawingContext,Brushstroke,doublestrokeThickness1,Brushfillnull);}接口的作用IMouseOverShape 为形状增加了鼠标悬停反馈能力让用户知道鼠标当前指向哪个形状。// 普通形状只有绘制能力publicinterfaceIShape{voidDraw(...);}// 可悬停形状增加悬停检测和绘制publicinterfaceIMouseOverShape:IHitableShape{voidDrawMouseOver(...);// 悬停时的绘制boolHit(...);// 检测鼠标是否在形状内继承自IHitableShape}️ MouseOverShapeBase 抽象类// 抽象基类为悬停形状提供默认实现// 继承自 CommonShapeBase通用形状基类// 实现 IMouseOverShape 接口publicabstractclassMouseOverShapeBase:CommonShapeBase,IMouseOverShape{// 实现悬停状态绘制// virtual 子类可以重写自定义悬停效果publicvirtualvoidDrawMouseOver(IViewview,DrawingContextdrawingContext,Brushstroke,doublestrokeThickness1,Brushfillnull){// 直接调用普通绘制方法// 这意味着默认情况下悬停样式和普通样式一样this.Draw(view,drawingContext,stroke,strokeThickness,fill);}// 实现命中检测判断鼠标是否在形状内// virtual 子类必须重写不同形状检测逻辑不同// 当前返回 false需要子类实现真正的检测逻辑publicvirtualboolHit(IViewview,Pointpoint){returnfalse;// 默认永远不命中}}关键理解这个基类有两个重要方法需要子类实现Hit()判断鼠标是否在形状内必须重写DrawMouseOver()绘制悬停效果可选重写// 子类实现示例publicclassRectangleShape:MouseOverShapeBase{publicRectBounds{get;set;}// 必须实现检测点是否在矩形内publicoverrideboolHit(IViewview,Pointpoint){returnBounds.Contains(point);}// 可选重写自定义悬停样式publicoverridevoidDrawMouseOver(IViewview,DrawingContextdc,Brushstroke,doublethickness,Brushfill){// 使用更醒目的样式varpennewPen(stroke,thickness2);// 更粗的边框dc.DrawRectangle(fill,pen,Bounds);}} 继承关系图IShape接口 ↓ IHitableShape接口← 命中检测 ↓ IMouseOverShape接口← 悬停功能 ← 当前接口 ↓ ShapeBase抽象类 ↓ CommonShapeBase抽象类← 通用功能 ↓ MouseOverShapeBase抽象类← 当前类 ↓ SelectableShapeBase抽象类← 选中功能 ↓ HandleShapeBase抽象类← 控制点功能 ↓ PreviewShapeBase抽象类← 预览功能 ↓ 具体形状 ├── RectangleShape矩形 ├── CircleShape圆形 ├── PolygonShape多边形 └── ... 更多 命中检测Hit的实现不同的形状需要实现不同的命中检测逻辑矩形命中检测publicclassRectangleShape:MouseOverShapeBase{publicRectBounds{get;set;}publicoverrideboolHit(IViewview,Pointpoint){// 矩形判断点是否在矩形范围内returnBounds.Contains(point);}}圆形命中检测publicclassCircleShape:MouseOverShapeBase{publicPointCenter{get;set;}publicdoubleRadius{get;set;}publicoverrideboolHit(IViewview,Pointpoint){// 圆形计算点到圆心的距离是否小于半径doubledxpoint.X-Center.X;doubledypoint.Y-Center.Y;doubledistanceMath.Sqrt(dx*dxdy*dy);returndistanceRadius;}}多边形命中检测射线法publicclassPolygonShape:MouseOverShapeBase{publicListPointPoints{get;set;}publicoverrideboolHit(IViewview,Pointpoint){// 射线法从点向右发射射线计算与多边形边的交点数量// 奇数表示在内部偶数表示在外部boolinsidefalse;intcountPoints.Count;for(inti0,jcount-1;icount;ji){varpiPoints[i];varpjPoints[j];boolintersect((pi.Ypoint.Y)!(pj.Ypoint.Y))(point.X(pj.X-pi.X)*(point.Y-pi.Y)/(pj.Y-pi.Y)pi.X);if(intersect)inside!inside;}returninside;}}线条命中检测点到线段距离publicclassLineShape:MouseOverShapeBase{publicPointStart{get;set;}publicPointEnd{get;set;}publicdoubleTolerance{get;set;}5;// 容差像素publicoverrideboolHit(IViewview,Pointpoint){// 计算点到线段的最短距离doubledistancepoint.DistanceToSegment(Start,End);// 距离小于容差则认为命中returndistanceTolerance;}} 悬停效果的自定义简单悬停效果加粗边框publicclassSimpleHoverRect:RectangleShape{publicoverridevoidDrawMouseOver(IViewview,DrawingContextdc,Brushstroke,doublethickness,Brushfill){// 加粗边框varpennewPen(stroke,thickness3);dc.DrawRectangle(fill,pen,Bounds);}}发光悬停效果publicclassGlowHoverRect:RectangleShape{publicoverridevoidDrawMouseOver(IViewview,DrawingContextdc,Brushstroke,doublethickness,Brushfill){// 先绘制外发光varglowPennewPen(newSolidColorBrush(Colors.Yellow){Opacity0.5},thickness6);dc.DrawRectangle(null,glowPen,newRect(Bounds.X-3,Bounds.Y-3,Bounds.Width6,Bounds.Height6));// 再绘制实际边框varpennewPen(stroke,thickness);dc.DrawRectangle(fill,pen,Bounds);}}半透明填充悬停效果publicclassTranslucentHoverRect:RectangleShape{publicoverridevoidDrawMouseOver(IViewview,DrawingContextdc,Brushstroke,doublethickness,Brushfill){// 使用半透明填充varsemiFillfill?.Clone();if(semiFillisSolidColorBrushsolid){solid.Opacity0.3;// 30%透明度}varpennewPen(stroke,thickness);dc.DrawRectangle(semiFill,pen,Bounds);}}虚线边框悬停效果publicclassDashedHoverRect:RectangleShape{publicoverridevoidDrawMouseOver(IViewview,DrawingContextdc,Brushstroke,doublethickness,Brushfill){// 使用虚线边框varpennewPen(stroke,thickness){DashStyleDashStyles.Dash// 虚线样式};dc.DrawRectangle(fill,pen,Bounds);}} 在 MouseOverShapeBox 中的使用回顾之前的MouseOverShapeBox代码publicclassMouseOverShapeBox:ShapeBox{protectedoverridevoidOnMouseMove(MouseEventArgse){base.OnMouseMove(e);if(this.Shapesnull)return;// 获取鼠标位置Pointpointe.GetPosition(this);// 查找所有鼠标下的形状只关心实现了 IMouseOverShape 的varfindsthis.Shapes.OfTypeIMouseOverShape().Where(xx.Hit(this,point));// 更新高亮显示this.MouseOverShapes(finds.ToArray());}privatevoidDrawMouseOverableShapes(){usingvardcthis._MouseOverableShapeDrawingVisual.RenderOpen();if(this._MouseOverShapes.Count0)return;varthicknessthis.ToViewThickness(this.MouseOverStrokeThickness);foreach(varshapeinthis._MouseOverShapes){// 调用悬停绘制方法shape.DrawMouseOver(this,dc,this.MouseOverStroke,// 悬停边框颜色thickness,// 悬停边框粗细this.MouseOverFill);// 悬停填充颜色}}} 工作流程用户移动鼠标 ↓ MouseOverShapeBox.OnMouseMove 触发 ↓ 获取鼠标坐标 ↓ 遍历所有形状筛选实现 IMouseOverShape 的 ↓ 调用每个形状的 Hit() 方法检测 ↓ 收集所有命中的形状 ↓ 调用 DrawMouseOver() 绘制高亮 ↓ 用户看到悬停效果 完整的形状类实现示例// 完整的可选择、可悬停矩形publicclassFullFeaturedRectangle:MouseOverShapeBase,ISelectableShape{publicRectBounds{get;set;}// 普通绘制publicoverridevoidDrawing(IViewview,DrawingContextdc,Penpen,Brushfill){dc.DrawRectangle(fill,pen,Bounds);}// 命中检测publicoverrideboolHit(IViewview,Pointpoint){returnBounds.Contains(point);}// 悬停绘制重写实现高亮publicoverridevoidDrawMouseOver(IViewview,DrawingContextdc,Brushstroke,doublethickness,Brushfill){// 使用更粗的黄色边框varpennewPen(stroke??Brushes.Yellow,thickness2);pen.Freeze();this.Drawing(view,dc,pen,fill);}// 选中绘制实现 ISelectableShapepublicvoidDrawSelect(IViewview,DrawingContextdc,Brushstroke,doublethickness,Brushfill){// 使用红色粗边框 半透明填充varpennewPen(stroke??Brushes.Red,thickness3);pen.Freeze();varsemiFillfill?.Clone()asSolidColorBrush;if(semiFill!null)semiFill.Opacity0.3;this.Drawing(view,dc,pen,semiFill);}}设计模式分析1. 模板方法模式publicvirtualboolHit(IViewview,Pointpoint){returnfalse;// 子类实现具体逻辑}父类定义框架子类实现具体算法。2. 策略模式不同的形状实现不同的Hit检测策略矩形检测、圆形检测、多边形检测等。3. 装饰器模式MouseOverShapeBase:CommonShapeBase一层层添加功能基础 → 悬停 → 选中 → 控制点 → 预览总结组件类型职责IMouseOverShape接口定义悬停检测和绘制能力MouseOverShapeBase抽象类提供悬停功能的默认框架核心功能Hit()检测鼠标是否在形状内必须由子类实现DrawMouseOver()绘制悬停效果可选重写这个设计让形状能够响应鼠标悬停为标注工具提供了即时视觉反馈大大提升了用户体验

更多文章