2026/4/6 16:14:06
网站建设
项目流程
MouseClickHandleMouseClickHandle 详解 - 鼠标点击控制点基类这是一个抽象基类为需要响应鼠标点击事件的控制点提供了默认实现。它继承自HandleBase基础控制点并实现IMouseClickHandle接口鼠标点击能力让子类可以方便地添加点击交互功能。 文件头部和引用usingSystem.Windows.Input;// 导入鼠标事件参数类MouseButtonEventArgsnamespaceH.LabelImg.ShapeBox.Shapes.Handles;// 命名空间存放形状的控制点相关类️ MouseClickHandle 抽象类// 抽象基类为需要响应鼠标点击的控制点提供默认实现// 继承自 HandleBase基础控制点功能// 实现 IMouseClickHandle 接口鼠标点击能力publicabstractclassMouseClickHandle:HandleBase,IMouseClickHandle{// 构造函数创建鼠标点击控制点// 参数// postion - 控制点的初始位置// shape - 控制点所属的形状publicMouseClickHandle(Pointpostion,IShapeshape):base(postion,shape){// 构造函数体为空// 所有初始化工作都在父类 HandleBase 中完成// 子类可以在这里添加自己的初始化逻辑}// 鼠标按下事件处理虚方法子类可重写// 参数// sender - 事件发送者通常是 ShapeBox 控件// e - 鼠标事件参数包含点击位置、按下的按钮等信息// virtual 子类可以选择重写此方法publicvirtualvoidMouseDown(objectsender,MouseButtonEventArgse){// 默认实现为空// 子类可以重写此方法来处理鼠标按下逻辑// 例如改变外观、记录拖拽起始点等}// 鼠标释放事件处理虚方法子类可重写// 参数// sender - 事件发送者// e - 鼠标事件参数// virtual 子类可以选择重写此方法publicvirtualvoidMouseUp(objectsender,MouseButtonEventArgse){// 默认实现为空// 子类可以重写此方法来处理鼠标释放逻辑// 例如执行命令、恢复外观等}} 设计目的为什么要提供这个基类如果没有这个基类每个需要鼠标点击功能的控制点都要// ❌ 没有基类每个类都要重复实现publicclassDeleteHandle:HandleBase,IMouseClickHandle{publicDeleteHandle(Pointposition,IShapeshape):base(position,shape){// 构造函数}publicvoidMouseDown(objectsender,MouseButtonEventArgse){// 每个类都要自己实现}publicvoidMouseUp(objectsender,MouseButtonEventArgse){// 每个类都要自己实现}}publicclassEditHandle:HandleBase,IMouseClickHandle{// 又要重复写同样的 MouseDown/MouseUp 空实现}// ✅ 有基类只写需要的部分publicclassDeleteHandle:MouseClickHandle{publicDeleteHandle(Pointposition,IShapeshape):base(position,shape){}// 只重写需要的方法publicoverridevoidMouseUp(objectsender,MouseButtonEventArgse){// 删除逻辑}// MouseDown 不需要重写使用基类的空实现}核心价值减少重复代码提供默认的空实现。 完整的子类实现示例示例1删除控制点重写 MouseUppublicclassDeleteHandle:MouseClickHandle{publicDeleteHandle(Pointposition,IShapeshape):base(position,shape){this.Length12;this.CursorCursors.Hand;}// 只重写 MouseUp单击释放时删除publicoverridevoidMouseUp(objectsender,MouseButtonEventArgse){if(e.ChangedButtonMouseButton.Left){// 确认删除if(MessageBox.Show(确定删除,确认,MessageBoxButton.YesNo)MessageBoxResult.Yes){// 从视图中移除形状if(senderisShapeBoxboxthis.OwnerisIShapeshape){box.Shapes.Remove(shape);}}}}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;dc.DrawCircle(this.Postion,pen,len,Brushes.Red);dc.DrawCross(this.Postion,Brushes.White,pen.Thickness,0.5*len,0.5*len,45);}}示例2编辑控制点重写 MouseDown 双击检测publicclassEditHandle:MouseClickHandle{privateDateTime_lastClickTime;privateconstdoubleDOUBLE_CLICK_INTERVAL500;// 毫秒publicEditHandle(Pointposition,IShapeshape):base(position,shape){this.Length10;this.CursorCursors.Pen;}// 重写 MouseDown 检测双击publicoverridevoidMouseDown(objectsender,MouseButtonEventArgse){if(e.ChangedButtonMouseButton.Left){DateTimenowDateTime.Now;if((now-_lastClickTime).TotalMillisecondsDOUBLE_CLICK_INTERVAL){// 双击打开编辑对话框OpenEditDialog();_lastClickTimeDateTime.MinValue;}else{// 单击记录时间等待可能的双击_lastClickTimenow;}}}privatevoidOpenEditDialog(){if(this.OwnerisRectangleShaperect){vardialognewEditRectangleDialog(rect);if(dialog.ShowDialog()true){// 更新形状rect.Boundsdialog.Bounds;}}}// MouseUp 不需要重写使用基类的空实现publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;dc.DrawCircle(this.Postion,pen,len,Brushes.LightBlue);// 绘制编辑图标铅笔dc.DrawEllipse(Brushes.Black,null,this.Postion,len*0.3,len*0.3);}}示例3右键菜单控制点重写 MouseUppublicclassContextMenuHandle:MouseClickHandle{privateContextMenu_contextMenu;publicContextMenuHandle(Pointposition,IShapeshape):base(position,shape){this.Length10;this.CursorCursors.Arrow;CreateContextMenu();}privatevoidCreateContextMenu(){_contextMenunewContextMenu();varcopyItemnewMenuItem{Header复制};copyItem.Click(s,e)CopyShape();vardeleteItemnewMenuItem{Header删除};deleteItem.Click(s,e)DeleteShape();_contextMenu.Items.Add(copyItem);_contextMenu.Items.Add(deleteItem);}// 重写 MouseUp 处理右键点击publicoverridevoidMouseUp(objectsender,MouseButtonEventArgse){if(e.ChangedButtonMouseButton.Right){// 显示右键菜单if(senderisUIElementelement){_contextMenu.PlacementTargetelement;_contextMenu.PlacementSystem.Windows.Controls.Primitives.PlacementMode.MousePoint;_contextMenu.IsOpentrue;}}}privatevoidCopyShape(){// 复制逻辑}privatevoidDeleteShape(){// 删除逻辑}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;dc.DrawCircle(this.Postion,pen,len,Brushes.LightGray);// 绘制三个点表示菜单doubledotSizelen*0.25;dc.DrawEllipse(Brushes.Black,null,newPoint(this.Postion.X,this.Postion.Y-dotSize),dotSize,dotSize);dc.DrawEllipse(Brushes.Black,null,newPoint(this.Postion.X,this.Postion.Y),dotSize,dotSize);dc.DrawEllipse(Brushes.Black,null,newPoint(this.Postion.X,this.Postion.YdotSize),dotSize,dotSize);}}示例4切换状态控制点重写 MouseDown 和 MouseUppublicclassToggleHandle:MouseClickHandle{privatebool_isToggled;privateBrush_normalColorBrushes.Gray;privateBrush_toggledColorBrushes.Green;publicToggleHandle(Pointposition,IShapeshape):base(position,shape){this.Length10;this.CursorCursors.Hand;}// 重写 MouseDown 改变外观publicoverridevoidMouseDown(objectsender,MouseButtonEventArgse){if(e.ChangedButtonMouseButton.Left){_isToggled!_isToggled;// 通知形状状态改变if(this.OwnerisITogglableShapetogglable){togglable.IsEnabled_isToggled;}}}// 可选重写 MouseUppublicoverridevoidMouseUp(objectsender,MouseButtonEventArgse){// 可以在这里添加额外的逻辑// 例如播放音效、记录日志等}publicoverridevoidDraw(IViewview,DrawingContextdc,Penpen,Brushfillnull){doublelen0.5*this.Length/view.Scale;// 根据状态改变颜色BrushbgBrush_isToggled?_toggledColor:_normalColor;dc.DrawCircle(this.Postion,pen,len,bgBrush);// 绘制开关图标竖线dc.DrawLine(newPen(Brushes.White,2),newPoint(this.Postion.X,this.Postion.Y-len*0.5),newPoint(this.Postion.X,this.Postion.Ylen*0.5));}}publicinterfaceITogglableShape{boolIsEnabled{get;set;}} 继承关系图IHandle接口 ↓ HandleBase抽象基类 ↓ MouseClickHandle当前抽象类← 添加鼠标点击能力 ↓ 具体控制点 ├── DeleteHandle删除 ├── EditHandle编辑 ├── ContextMenuHandle右键菜单 ├── ToggleHandle切换开关 └── ... 更多 高级用法链式调用publicclassChainClickHandle:MouseClickHandle{privateListActionMouseButtonEventArgs_mouseDownActionsnewListActionMouseButtonEventArgs();privateListActionMouseButtonEventArgs_mouseUpActionsnewListActionMouseButtonEventArgs();publicChainClickHandle(Pointposition,IShapeshape):base(position,shape){}publicvoidAddMouseDownAction(ActionMouseButtonEventArgsaction){_mouseDownActions.Add(action);}publicvoidAddMouseUpAction(ActionMouseButtonEventArgsaction){_mouseUpActions.Add(action);}publicoverridevoidMouseDown(objectsender,MouseButtonEventArgse){// 执行所有注册的按下动作foreach(varactionin_mouseDownActions){action?.Invoke(e);}}publicoverridevoidMouseUp(objectsender,MouseButtonEventArgse){// 执行所有注册的释放动作foreach(varactionin_mouseUpActions){action?.Invoke(e);}}}// 使用varhandlenewChainClickHandle(position,shape);handle.AddMouseDownAction(eConsole.WriteLine(按下));handle.AddMouseUpAction(eConsole.WriteLine(释放));handle.AddMouseUpAction(eDeleteShape()); 在 SelectShapeBox 中的集成publicclassSelectShapeBox:MouseOverShapeBox{protectedoverridevoidOnMouseDown(MouseButtonEventArgse){Pointpointe.GetPosition(this);// 查找鼠标点击控制点foreach(varshapeinShapes.OfTypeIHandleShape()){varhandleshape.HitIHandle(this,point);if(handleisIMouseClickHandleclickHandle){// 调用 MouseDownclickHandle.MouseDown(this,e);if(e.Handled)return;}}base.OnMouseDown(e);}protectedoverridevoidOnMouseUp(MouseButtonEventArgse){Pointpointe.GetPosition(this);// 查找鼠标点击控制点foreach(varshapeinShapes.OfTypeIHandleShape()){varhandleshape.HitIHandle(this,point);if(handleisIMouseClickHandleclickHandle){// 调用 MouseUpclickHandle.MouseUp(this,e);if(e.Handled)return;}}base.OnMouseUp(e);}}设计模式分析1. 模板方法模式基类定义了鼠标事件处理的框架子类重写具体行为。2. 默认适配器模式提供空实现的虚方法子类只需重写需要的方法。3. 继承复用通过继承获得基础功能减少重复代码。总结成员类型用途MouseClickHandle()构造函数创建控制点实例MouseDown()虚方法鼠标按下处理默认空MouseUp()虚方法鼠标释放处理默认空核心优势减少重复代码不需要每个类都写空实现灵活扩展子类可以只重写需要的方法统一接口所有点击控制点都继承自同一个基类易于维护修改基类可以影响所有子类典型子类✅ DeleteHandle只重写 MouseUp✅ EditHandle只重写 MouseDown 检测双击✅ ContextMenuHandle只重写 MouseUp 显示菜单✅ ToggleHandle重写 MouseDown 切换状态这个基类是典型的模板类设计为鼠标点击功能提供了便捷的实现基础