MoveToHandleBase
2026/4/6 16:14:05 网站建设 项目流程
MoveToHandleBase 详解 - 可移动控制点基类这是一个抽象基类为需要支持拖拽移动的控制点提供了标准框架。它继承自HandleBase基础控制点并实现IMoveToHandle接口移动能力强制子类实现具体的移动逻辑。 文件头部和命名空间namespaceH.LabelImg.ShapeBox.Shapes.Handles;// 命名空间存放形状的控制点相关类️ MoveToHandleBase 抽象类// 抽象基类为需要支持拖拽移动的控制点提供标准框架// 继承自 HandleBase基础控制点功能位置、大小、绘制、命中检测// 实现 IMoveToHandle 接口移动能力MoveTo 方法publicabstractclassMoveToHandleBase:HandleBase,IMoveToHandle{// 构造函数创建可移动控制点// 参数// postion - 控制点的初始位置// shape - 控制点所属的形状publicMoveToHandleBase(Pointpostion,IShapeshape):base(postion,shape){// 构造函数体为空// 所有初始化工作都在父类 HandleBase 中完成// 子类可以在这里添加自己的初始化逻辑}// 抽象方法移动到指定位置// abstract 子类必须实现此方法强制要求// 参数// point - 要移动到的目标位置// 子类需要实现// 1. 更新控制点自身的位置// 2. 更新所属形状的属性大小、位置等publicabstractvoidMoveTo(Pointpoint);} 设计目的为什么要提供这个基类MoveToHandleBase与MouseClickHandle类似都是为了减少重复代码但针对的是移动能力而不是点击能力。与IMoveToHandle接口的关系// 接口只定义能做什么publicinterfaceIMoveToHandle:IHandle{voidMoveTo(Pointpoint);// 声明能力}// 抽象基类提供框架但不提供具体实现publicabstractclassMoveToHandleBase:HandleBase,IMoveToHandle{publicabstractvoidMoveTo(Pointpoint);// 强制子类实现}// 具体类提供具体实现publicclassMoveHandle:MoveToHandleBase{publicoverridevoidMoveTo(Pointpoint){// 具体的移动逻辑}}三层结构的好处层级类型职责是否必须实现IHandle接口定义控制点基础能力必须IMoveToHandle接口定义移动能力必须MoveToHandleBase抽象类提供基础框架继承即可具体控制点类实现具体移动逻辑必须实现 MoveTo 完整的子类实现示例示例1移动形状的控制点// 用于移动整个形状的控制点publicclassMoveHandle:MoveToHandleBase{privateIShape_owner;publicMoveHandle(Pointposition,IShapeshape):base(position,shape){_ownershape;this.Length8;this.CursorCursors.SizeAll;// 四向箭头}// 必须实现 MoveTo 方法publicoverridevoidMoveTo(Pointpoint){// 1. 计算移动偏移量Vectoroffsetpoint-this.Postion;// 2. 更新控制点自身位置this.Postionpoint;// 3. 更新所属形状的位置if(_ownerisRectangleShaperect){rect.BoundsnewRect(rect.Bounds.Xoffset.X,rect.Bounds.Yoffset.Y,rect.Bounds.Width,rect.Bounds.Height);}elseif(_ownerisCircleShapecircle){circle.CenternewPoint(circle.Center.Xoffset.X,circle.Center.Yoffset.Y);}elseif(_ownerisTextShapetext){text.PositionnewPoint(text.Position.Xoffset.X,text.Position.Yoffset.Y);}}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;// 绘制移动图标dc.DrawRectangle(fill??Brushes.White,pen,newRect(this.Postion.X-len,this.Postion.Y-len,len*2,len*2));// 绘制四个方向箭头简化为十字线dc.DrawLine(pen,newPoint(this.Postion.X-len,this.Postion.Y),newPoint(this.Postion.Xlen,this.Postion.Y));dc.DrawLine(pen,newPoint(this.Postion.X,this.Postion.Y-len),newPoint(this.Postion.X,this.Postion.Ylen));}}示例2缩放形状的控制点角点// 用于改变形状大小的控制点右下角publicclassResizeHandle:MoveToHandleBase{privateRectangleShape_rectangle;publicResizeHandle(RectangleShaperectangle,Pointposition):base(position,rectangle){_rectanglerectangle;this.Length8;this.CursorCursors.SizeNWSE;// 对角线箭头}publicoverridevoidMoveTo(Pointpoint){Rectbounds_rectangle.Bounds;// 根据新位置调整矩形大小右下角拖拽boundsnewRect(bounds.Left,// X 不变bounds.Top,// Y 不变point.X-bounds.Left,// 新宽度point.Y-bounds.Top// 新高度);// 确保宽高为正数if(bounds.Width0bounds.Height0){_rectangle.Boundsbounds;// 更新控制点位置到新的角点this.PostionnewPoint(bounds.Right,bounds.Bottom);}}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;dc.DrawRectangle(fill??Brushes.White,pen,newRect(this.Postion.X-len,this.Postion.Y-len,len*2,len*2));}}示例3约束移动只能水平或垂直// 只能水平移动的控制点publicclassHorizontalMoveHandle:MoveToHandleBase{privateIShape_owner;privatedouble_originalY;publicHorizontalMoveHandle(Pointposition,IShapeshape):base(position,shape){_ownershape;_originalYposition.Y;this.Length8;this.CursorCursors.SizeWE;// 水平双箭头}publicoverridevoidMoveTo(Pointpoint){// 只使用 X 坐标Y 坐标保持不变PointconstrainedPointnewPoint(point.X,_originalY);// 计算移动偏移VectoroffsetconstrainedPoint-this.Postion;// 更新控制点位置this.PostionconstrainedPoint;// 更新形状只水平移动if(_ownerisRectangleShaperect){rect.BoundsnewRect(rect.Bounds.Xoffset.X,rect.Bounds.Y,rect.Bounds.Width,rect.Bounds.Height);}}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;dc.DrawRectangle(fill??Brushes.LightBlue,pen,newRect(this.Postion.X-len,this.Postion.Y-len,len*2,len*2));// 绘制水平箭头指示dc.DrawLine(pen,newPoint(this.Postion.X-len*1.5,this.Postion.Y),newPoint(this.Postion.Xlen*1.5,this.Postion.Y));}}示例4旋转形状的控制点// 用于旋转形状的控制点publicclassRotateHandle:MoveToHandleBase{privateIShape_owner;privatePoint_center;privatedouble_originalRadius;publicRotateHandle(Pointposition,IShapeshape,Pointcenter):base(position,shape){_ownershape;_centercenter;_originalRadiusMath.Sqrt(Math.Pow(position.X-center.X,2)Math.Pow(position.Y-center.Y,2));this.Length10;this.CursorCursors.Hand;}publicoverridevoidMoveTo(Pointpoint){// 计算从中心到新位置的角度doubledxpoint.X-_center.X;doubledypoint.Y-_center.Y;doubleangleMath.Atan2(dy,dx);// 计算新的控制点位置保持半径不变PointnewPositionnewPoint(_center.X_originalRadius*Math.Cos(angle),_center.Y_originalRadius*Math.Sin(angle));// 更新控制点位置this.PostionnewPosition;// 更新形状的旋转角度if(_ownerisIRotatableShaperotatable){rotatable.RotationAngleangle*180/Math.PI;}}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;// 绘制圆形旋转控制点dc.DrawEllipse(fill??Brushes.White,pen,this.Postion,len,len);// 绘制连接线从中心到控制点dc.DrawLine(pen,_center,this.Postion);}}publicinterfaceIRotatableShape{doubleRotationAngle{get;set;}}示例5智能吸附控制点// 带吸附功能的移动控制点publicclassSnapMoveHandle:MoveToHandleBase{privateIShape_owner;privateListPoint_snapPoints;privatedouble_snapDistance10;publicSnapMoveHandle(Pointposition,IShapeshape,ListPointsnapPoints):base(position,shape){_ownershape;_snapPointssnapPoints;this.Length8;this.CursorCursors.SizeAll;}publicoverridevoidMoveTo(Pointpoint){// 查找最近的吸附点PointsnappedPointFindSnapPoint(point);// 计算移动偏移VectoroffsetsnappedPoint-this.Postion;// 更新控制点位置this.PostionsnappedPoint;// 更新形状位置if(_ownerisRectangleShaperect){rect.BoundsnewRect(rect.Bounds.Xoffset.X,rect.Bounds.Yoffset.Y,rect.Bounds.Width,rect.Bounds.Height);}}privatePointFindSnapPoint(Pointpoint){PointbestPointpoint;doublebestDistance_snapDistance;foreach(varsnapPointin_snapPoints){doubledistanceMath.Sqrt(Math.Pow(point.X-snapPoint.X,2)Math.Pow(point.Y-snapPoint.Y,2));if(distancebestDistance){bestPointsnapPoint;bestDistancedistance;}}returnbestPoint;}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;dc.DrawRectangle(fill??Brushes.Gold,pen,newRect(this.Postion.X-len,this.Postion.Y-len,len*2,len*2));}} 在 SelectShapeBox 中的集成publicclassSelectShapeBox:MouseOverShapeBox{privateMoveToHandleBase_activeMoveHandle;privatePoint_dragStartPoint;protectedoverridevoidOnMouseDown(MouseButtonEventArgse){Pointpointe.GetPosition(this);// 查找可移动的控制点foreach(varshapeinShapes.OfTypeIHandleShape()){varhandleshape.HitIHandle(this,point);if(handleisMoveToHandleBasemoveHandle){_activeMoveHandlemoveHandle;_dragStartPointpoint;// 可选记录起始状态用于撤销/重做RecordStartState(moveHandle.Owner);e.Handledtrue;return;}}base.OnMouseDown(e);}protectedoverridevoidOnMouseMove(MouseEventArgse){if(_activeMoveHandle!null){PointcurrentPointe.GetPosition(this);// 调用 MoveTo 方法_activeMoveHandle.MoveTo(currentPoint);// 重绘this.DrawShapes();this.DrawSelectableShapes();e.Handledtrue;}else{base.OnMouseMove(e);}}protectedoverridevoidOnMouseUp(MouseButtonEventArgse){if(_activeMoveHandle!null){// 记录结束状态用于撤销/重做RecordEndState(_activeMoveHandle.Owner);_activeMoveHandlenull;e.Handledtrue;}else{base.OnMouseUp(e);}}} 与其他基类的对比基类继承自实现接口核心方法用途HandleBase无直接实现 IHandleIHandleDraw(),HitTestPoint()基础控制点MouseClickHandleHandleBaseIMouseClickHandleMouseDown(),MouseUp()点击交互MoveToHandleBaseHandleBaseIMoveToHandleMoveTo()拖拽移动设计模式分析1. 模板方法模式基类定义了移动控制点的框架子类实现具体的移动逻辑。2. 强制实现模式抽象方法使用abstract关键字强制子类实现MoveTo方法确保所有子类都有移动能力。3. 接口隔离原则将移动能力分离为独立的接口和基类。总结成员类型用途MoveToHandleBase()构造函数创建控制点实例MoveTo(Point)抽象方法必须实现移动到新位置核心优势强制实现abstract确保子类必须提供移动逻辑统一框架所有移动控制点使用相同的基类减少重复不需要每个类都重复声明IMoveToHandle接口类型安全可以安全地使用is MoveToHandleBase检测典型子类✅ MoveHandle移动形状✅ ResizeHandle缩放形状✅ RotateHandle旋转形状✅ HorizontalMoveHandle约束移动✅ SnapMoveHandle吸附移动这个基类是拖拽交互的标准框架为所有需要移动能力的控制点提供了坚实的基础

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询