StateShapeBox

张开发
2026/6/10 8:47:10 15 分钟阅读
StateShapeBox
StateShapeBox四种控件的功能对比功能ShapeBoxMouseOverShapeBoxSelectShapeBoxStateShapeBox显示图片✅✅✅✅绘制形状✅✅✅✅鼠标悬停高亮❌✅✅✅选中高亮❌❌✅✅状态机交互❌❌❌✅操作预览❌❌❌✅鼠标事件路由❌❌❌✅这个控件是一个完整的状态机驱动的标注系统为复杂交互提供了坚实的基础架构StateShapeBox 源码详解 - 状态机驱动的标注框控件这是一个支持状态机模式的图像标注控件可以根据不同的操作状态绘制、编辑、移动等改变交互行为和视觉样式。类似于绘图软件中切换工具画笔、选择、橡皮擦的效果。 文件头部和引用// 版权信息同前// Copyright (c) HeBianGu Authors. All Rights Reserved.// ...省略usingH.Extensions.Common;// 扩展方法ToEnumerable等usingSystem.Windows.Input;// 键盘/鼠标输入Keyboard.Modifiers等 接口定义// 定义状态形状视图的能力publicinterfaceIStateShapeView:IView{// 绘制状态形状参数可变数量的形状voidDrawStateShape(paramsIShape[]shapes);}通俗理解这是一个合同规定实现者需要能够根据当前状态绘制特定的形状比如绘制新形状时的预览效果。️ 类定义// 继承 SelectShapeBox可选择高亮实现 IStateShapeView状态视图// 功能层级ShapeBox → MouseOverShapeBox → SelectShapeBox → StateShapeBox// 基础显示 → 悬停高亮 → 选中高亮 → 状态机交互publicclassStateShapeBox:SelectShapeBox,IStateShapeView继承关系图ShapeBox基础图片/形状显示 ↓ MouseOverShapeBox鼠标悬停高亮 ↓ SelectShapeBox选中高亮 ↓ StateShapeBox状态机交互← 当前类 私有字段// 专门用来绘制状态形状的视觉对象// 例如正在绘制新矩形时的预览效果privateDrawingVisual_StateShapeDrawingVisualnewDrawingVisual();完整图层结构从下到上┌─────────────────────────────────┐ │ 状态图层StateShapeDrawingVisual│ ← 当前操作的预览最上层 ├─────────────────────────────────┤ │ 选中图层SelectableShapeDrawingVisual│ ← 红色边框选中 ├─────────────────────────────────┤ │ 悬停图层MouseOverableShapeDrawingVisual│ ← 黄色边框悬停 ├─────────────────────────────────┤ │ 普通形状层ShapeDrawingVisual │ ← 蓝色边框普通 ├─────────────────────────────────┤ │ 图片层ImageDrawingVisual │ ← 背景图片 └─────────────────────────────────┘ 构造函数publicStateShapeBox(){// 订阅鼠标事件this.MouseDownthis.StateShapeBox_MouseDown;// 鼠标按下this.MouseMovethis.StateShapeBox_MouseMove;// 鼠标移动this.MouseUpthis.StateShapeBox_MouseUp;// 鼠标释放this.MouseLeavethis.StateShapeBox_MouseLeave;// 鼠标离开}为什么订阅这些事件状态机需要知道用户的操作来切换状态或执行相应行为。️ 鼠标离开事件privatevoidStateShapeBox_MouseLeave(objectsender,System.Windows.Input.MouseEventArgse){// 检查当前状态的修饰键是否匹配例如Ctrl、Shift等if(this.GetState()?.ModifierKeys!Keyboard.Modifiers)return;// 不匹配就不处理// 调用当前状态的鼠标离开处理方法this.GetState()?.MouseLeave(sender,e);}ModifierKeys 解释Keyboard.Modifiers 当前按下的修饰键Ctrl、Alt、Shift等例如只有在按住Ctrl时拖拽才是复制操作️ 鼠标释放事件privatevoidStateShapeBox_MouseUp(objectsender,System.Windows.Input.MouseButtonEventArgse){// 检查修饰键匹配if(this.GetState()?.ModifierKeys!Keyboard.Modifiers)return;// 调用当前状态的鼠标释放处理this.GetState()?.MouseUp(sender,e);}典型用途完成绘制形状鼠标释放时把预览形状变成正式形状。️ 鼠标移动事件privatevoidStateShapeBox_MouseMove(objectsender,System.Windows.Input.MouseEventArgse){if(this.GetState()?.ModifierKeys!Keyboard.Modifiers)return;this.GetState()?.MouseMove(sender,e);}典型用途更新绘制预览鼠标移动时实时显示将要绘制的形状。️ 鼠标按下事件privatevoidStateShapeBox_MouseDown(objectsender,System.Windows.Input.MouseButtonEventArgse){if(this.GetState()?.ModifierKeys!Keyboard.Modifiers)return;this.GetState()?.MouseDown(sender,e);}典型用途开始绘制形状记录起始点。️ 重写创建视觉对象protectedoverrideIEnumerableVisualCreateVisuals(){// 在父类图层基础上添加状态图层// 父类返回[图片层, 普通形状层, 悬停图层, 选中图层]// 加上状态图层放在最上面returnbase.CreateVisuals().Concat(this._StateShapeDrawingVisual.ToEnumerable());} 重写缩放变化处理protectedoverridevoidOnScaleChanged(){// 调用父类方法base.OnScaleChanged();// 通知当前状态缩放变了状态可能需要重新计算坐标this.ViewState?.ScaleChanged();} 获取当前状态// 虚方法子类可以重写protectedvirtualIStateGetState(){returnthis.ViewState;// 返回当前视图状态}IState 接口定义了状态的通用行为MouseDown、MouseMove、MouseUp等。 视图状态属性核心publicIViewStateViewState{get{return(IViewState)GetValue(ViewStateProperty);}set{SetValue(ViewStateProperty,value);}}publicstaticreadonlyDependencyPropertyViewStatePropertyDependencyProperty.Register(ViewState,typeof(IViewState),typeof(StateShapeBox),newFrameworkPropertyMetadata(default(IViewState),(d,e){StateShapeBoxcontroldasStateShapeBox;if(controlnull)return;// 退出旧状态if(e.OldValueisIViewStateo){o.Exit();// 调用旧状态的退出方法}// 进入新状态if(e.NewValueisIViewStaten){n.Viewcontrol;// 设置状态的视图引用n.Enter();// 调用新状态的进入方法}}));状态机工作流程设置 ViewState 绘制状态 ↓ 旧状态.Exit()清理 ↓ 新状态.View 当前控件 ↓ 新状态.Enter()初始化 ↓ 用户鼠标操作 ↓ 当前状态处理鼠标事件 ↓ 可能切换状态或更新UI 状态边框颜色属性// 状态绘制时的边框颜色默认黄绿色publicBrushStateStroke{get{return(Brush)GetValue(StateStrokeProperty);}set{SetValue(StateStrokeProperty,value);}}publicstaticreadonlyDependencyPropertyStateStrokePropertyDependencyProperty.Register(StateStroke,typeof(Brush),typeof(StateShapeBox),newFrameworkPropertyMetadata(Brushes.Chartreuse,(d,e){})); 状态边框粗细属性// 状态绘制时的边框粗细默认1.0像素publicdoubleStateStrokeThickness{get{return(double)GetValue(StateStrokeThicknessProperty);}set{SetValue(StateStrokeThicknessProperty,value);}}publicstaticreadonlyDependencyPropertyStateStrokeThicknessPropertyDependencyProperty.Register(StateStrokeThickness,typeof(double),typeof(StateShapeBox),newFrameworkPropertyMetadata(1.0,(d,e){})); 状态填充颜色属性// 状态绘制时的填充颜色publicBrushStateFill{get{return(Brush)GetValue(StateFillProperty);}set{SetValue(StateFillProperty,value);}}publicstaticreadonlyDependencyPropertyStateFillPropertyDependencyProperty.Register(StateFill,typeof(Brush),typeof(StateShapeBox),newFrameworkPropertyMetadata(default(Brush),(d,e){}));✏️ 绘制状态形状核心方法// 实现 IStateShapeView 接口publicvoidDrawStateShape(paramsIShape[]shapes){// 打开状态图层的绘制上下文usingvardrawingContextthis._StateShapeDrawingVisual.RenderOpen();// 如果没有需要绘制的形状直接返回清除之前的状态预览if(shapesnull||shapes.Length0)return;// 计算视图中的边框粗细考虑缩放doublestrokeThicknessthis.ToViewThickness(this.StateStrokeThickness);// 遍历每个状态形状foreach(varshapeinshapes){if(shapenull)continue;// 使用状态样式绘制形状shape.Draw(this,drawingContext,this.StateStroke,strokeThickness,this.StateFill);}}使用场景绘制新形状时的预览效果鼠标拖拽时实时显示将要创建的矩形移动形状时的预览变换形状时的临时显示 总体设计思路完整的控件继承体系FrameworkElement ↓ ShapeBox基础显示 ├── 图片显示 ├── 形状绘制 └── 缩放支持 ↓ MouseOverShapeBox悬停高亮 ├── 鼠标检测 ├── 悬停样式 └── IMouseOverShape ↓ SelectShapeBox选中高亮 ├── 选中样式 ├── 选中集合 └── ISelectableShape ↓ StateShapeBox状态机← 当前类 ├── 状态图层 ├── 状态样式 ├── 鼠标事件路由 ├── 状态切换 └── IViewState状态机示例 - 绘制矩形状态publicclassDrawRectangleState:IViewState{privatePoint_startPoint;privateRectangleShape_preview;publicvoidEnter(){// 初始化状态_previewnewRectangleShape();}publicvoidExit(){// 清理状态_previewnull;}publicvoidMouseDown(StateShapeBoxview,MouseEventArgse){// 记录起始点_startPointe.GetPosition(view);}publicvoidMouseMove(StateShapeBoxview,MouseEventArgse){if(e.LeftButtonMouseButtonState.Pressed){// 更新预览矩形varcurrente.GetPosition(view);_preview.BoundsnewRect(_startPoint,current);// 绘制状态形状预览view.DrawStateShape(_preview);}}publicvoidMouseUp(StateShapeBoxview,MouseEventArgse){// 完成绘制添加正式形状view.Shapes.Add(_preview.Clone());// 清除预览view.DrawStateShape();// 可以切换到选择状态view.ViewStatenewSelectState();}}工作流程用户选择绘制矩形工具 ↓ ViewState new DrawRectangleState() ↓ 旧状态.Exit() 清理 新状态.Enter() 初始化 ↓ 用户鼠标按下 → 记录起始点 用户鼠标移动 → 更新预览 → DrawStateShape() 用户鼠标释放 → 添加正式形状 → 清除预览 ↓ 可能自动切换到其他状态完整使用示例// 创建状态标注框varboxnewStateShapeBox();box.ImageSourcemyImage;// 配置各种样式// 普通样式box.StrokeBrushes.Blue;box.StrokeThickness1;// 悬停样式box.MouseOverStrokeBrushes.Yellow;box.MouseOverStrokeThickness2;// 选中样式box.SelectStrokeBrushes.Red;box.SelectStrokeThickness3;// 状态预览样式绘制中的预览box.StateStrokeBrushes.Green;box.StateStrokeThickness1;box.StateFillBrushes.Transparent;// 切换到绘制矩形状态box.ViewStatenewDrawRectangleState();// 用户现在可以在图片上拖拽绘制矩形// 鼠标拖拽时显示绿色预览框// 释放鼠标后正式添加矩形状态类设计IViewStatepublicinterfaceIViewState{// 状态所属的视图StateShapeBoxView{get;set;}// 需要的修饰键Ctrl/Alt/ShiftModifierKeysModifierKeys{get;}// 生命周期voidEnter();// 进入状态时调用voidExit();// 退出状态时调用// 鼠标事件处理voidMouseDown(objectsender,MouseButtonEventArgse);voidMouseMove(objectsender,MouseEventArgse);voidMouseUp(objectsender,MouseButtonEventArgse);voidMouseLeave(objectsender,MouseEventArgse);// 其他事件...voidScaleChanged();}设计模式识别状态模式核心设计不同操作对应不同状态装饰器模式层层添加功能策略模式不同状态有不同行为策略观察者模式鼠标事件触发状态行为模板方法模式虚方法供子类扩展实际应用场景状态用途视觉效果绘制状态绘制新形状显示绿色预览框选择状态点击选择形状显示红色边框移动状态移动选中的形状显示虚线轮廓缩放状态调整形状大小显示控制点旋转状态旋转形状显示旋转手柄橡皮擦状态删除形状显示半透明覆盖四种控件的功能对比功能ShapeBoxMouseOverShapeBoxSelectShapeBoxStateShapeBox显示图片✅✅✅✅绘制形状✅✅✅✅鼠标悬停高亮❌✅✅✅选中高亮❌❌✅✅状态机交互❌❌❌✅操作预览❌❌❌✅鼠标事件路由❌❌❌✅这个控件是一个完整的状态机驱动的标注系统为复杂交互提供了坚实的基础架构

更多文章