diff --git a/XP.Common/Controls/Themes/Generic.xaml b/XP.Common/Controls/Themes/Generic.xaml deleted file mode 100644 index c48a889..0000000 --- a/XP.Common/Controls/Themes/Generic.xaml +++ /dev/null @@ -1,219 +0,0 @@ - - - - - diff --git a/XP.Common/Controls/VirtualJoystick.cs b/XP.Common/Controls/VirtualJoystick.cs index 50d8994..14fc43b 100644 --- a/XP.Common/Controls/VirtualJoystick.cs +++ b/XP.Common/Controls/VirtualJoystick.cs @@ -3,125 +3,95 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Shapes; namespace XP.Common.Controls { /// - /// 虚拟摇杆自定义控件,提供圆形区域内的鼠标拖拽操控能力 | Virtual joystick custom control providing mouse drag interaction within a circular area + /// 虚拟摇杆 UserControl,提供圆形区域内的鼠标拖拽操控能力 | Virtual joystick UserControl providing mouse drag interaction within a circular area /// /// /// 支持双轴/单轴 Y 模式、死区配置、归一化比例输出(-1.0 ~ 1.0)、鼠标左右键区分及四向功能图标切换。 /// Supports dual-axis/single-axis Y mode, dead zone configuration, normalized proportional output (-1.0 ~ 1.0), /// left/right mouse button differentiation, and four-directional icon switching. /// - [TemplatePart(Name = PART_Thumb, Type = typeof(UIElement))] - public class VirtualJoystick : Control + public partial class VirtualJoystick : UserControl { - /// - /// 操控点模板部件名称 | Thumb template part name - /// - private const string PART_Thumb = "PART_Thumb"; - #region 私有字段 | Private Fields - /// - /// 是否正在拖拽 | Whether dragging is in progress - /// + /// 是否正在拖拽 | Whether dragging is in progress private bool _isDragging; - /// - /// 控件中心点坐标 | Control center point coordinates - /// + /// 控件中心点坐标 | Control center point coordinates private Point _centerPoint; - /// - /// 操控点的平移变换,用于跟随鼠标移动 | Translate transform for thumb element to follow mouse movement - /// - private TranslateTransform _thumbTransform = new TranslateTransform(); + /// 操控点的平移变换 | Translate transform for thumb element + private readonly TranslateTransform _thumbTransform = new TranslateTransform(); + + /// 操控点元素引用 | Thumb element reference + private Ellipse? _thumbElement; #endregion - #region 静态构造函数 | Static Constructor - static VirtualJoystick() + #region 构造函数 | Constructor + + public VirtualJoystick() { - DefaultStyleKeyProperty.OverrideMetadata( - typeof(VirtualJoystick), - new FrameworkPropertyMetadata(typeof(VirtualJoystick))); + InitializeComponent(); + + // 控件加载完成后绑定操控点的 TranslateTransform | Bind thumb TranslateTransform after control loaded + Loaded += (s, e) => + { + _thumbElement = FindName("PART_Thumb") as Ellipse; + if (_thumbElement != null) + _thumbElement.RenderTransform = _thumbTransform; + + // 初始化时更新背景和图标可见性 | Update background and icon visibility on init + UpdateIconVisibility(); + }; } #endregion #region JoystickMode 依赖属性 | JoystickMode Dependency Property - /// - /// 摇杆轴模式依赖属性 | Joystick axis mode dependency property - /// public static readonly DependencyProperty JoystickModeProperty = - DependencyProperty.Register( - nameof(JoystickMode), - typeof(JoystickMode), - typeof(VirtualJoystick), - new PropertyMetadata(JoystickMode.DualAxis)); + DependencyProperty.Register(nameof(JoystickMode), typeof(JoystickMode), typeof(VirtualJoystick), + new PropertyMetadata(JoystickMode.DualAxis, OnJoystickModeChanged)); - /// - /// 获取或设置摇杆轴模式(双轴或单轴 Y)| Gets or sets the joystick axis mode (DualAxis or SingleAxisY) - /// + /// 获取或设置摇杆轴模式 | Gets or sets the joystick axis mode public JoystickMode JoystickMode { get => (JoystickMode)GetValue(JoystickModeProperty); set => SetValue(JoystickModeProperty, value); } + private static void OnJoystickModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is VirtualJoystick joystick) + joystick.UpdateIconVisibility(); + } + #endregion - #region OutputX 只读依赖属性 | OutputX Read-Only Dependency Property + #region OutputX/OutputY 只读依赖属性 | OutputX/OutputY Read-Only Dependency Properties - /// - /// OutputX 只读依赖属性键 | OutputX read-only dependency property key - /// private static readonly DependencyPropertyKey OutputXPropertyKey = - DependencyProperty.RegisterReadOnly( - nameof(OutputX), - typeof(double), - typeof(VirtualJoystick), - new PropertyMetadata(0.0)); - - /// - /// X 轴归一化输出依赖属性 | X-axis normalized output dependency property - /// + DependencyProperty.RegisterReadOnly(nameof(OutputX), typeof(double), typeof(VirtualJoystick), new PropertyMetadata(0.0)); public static readonly DependencyProperty OutputXProperty = OutputXPropertyKey.DependencyProperty; - /// - /// 获取 X 轴归一化输出值,范围 [-1.0, 1.0] | Gets the X-axis normalized output value, range [-1.0, 1.0] - /// + /// X 轴归一化输出值 [-1.0, 1.0] | X-axis normalized output public double OutputX { get => (double)GetValue(OutputXProperty); internal set => SetValue(OutputXPropertyKey, value); } - #endregion - - #region OutputY 只读依赖属性 | OutputY Read-Only Dependency Property - - /// - /// OutputY 只读依赖属性键 | OutputY read-only dependency property key - /// private static readonly DependencyPropertyKey OutputYPropertyKey = - DependencyProperty.RegisterReadOnly( - nameof(OutputY), - typeof(double), - typeof(VirtualJoystick), - new PropertyMetadata(0.0)); - - /// - /// Y 轴归一化输出依赖属性 | Y-axis normalized output dependency property - /// + DependencyProperty.RegisterReadOnly(nameof(OutputY), typeof(double), typeof(VirtualJoystick), new PropertyMetadata(0.0)); public static readonly DependencyProperty OutputYProperty = OutputYPropertyKey.DependencyProperty; - /// - /// 获取 Y 轴归一化输出值,范围 [-1.0, 1.0] | Gets the Y-axis normalized output value, range [-1.0, 1.0] - /// + /// Y 轴归一化输出值 [-1.0, 1.0] | Y-axis normalized output public double OutputY { get => (double)GetValue(OutputYProperty); @@ -132,19 +102,10 @@ namespace XP.Common.Controls #region DeadZone 依赖属性 | DeadZone Dependency Property - /// - /// 死区比例依赖属性 | Dead zone ratio dependency property - /// public static readonly DependencyProperty DeadZoneProperty = - DependencyProperty.Register( - nameof(DeadZone), - typeof(double), - typeof(VirtualJoystick), - new PropertyMetadata(0.05)); + DependencyProperty.Register(nameof(DeadZone), typeof(double), typeof(VirtualJoystick), new PropertyMetadata(0.05)); - /// - /// 获取或设置死区比例,范围 [0.0, 1.0],默认值 0.05 | Gets or sets the dead zone ratio, range [0.0, 1.0], default 0.05 - /// + /// 死区比例 [0.0, 1.0],默认 0.05 | Dead zone ratio, default 0.05 public double DeadZone { get => (double)GetValue(DeadZoneProperty); @@ -155,407 +116,132 @@ namespace XP.Common.Controls #region ActiveMouseButton 只读依赖属性 | ActiveMouseButton Read-Only Dependency Property - /// - /// ActiveMouseButton 只读依赖属性键 | ActiveMouseButton read-only dependency property key - /// private static readonly DependencyPropertyKey ActiveMouseButtonPropertyKey = - DependencyProperty.RegisterReadOnly( - nameof(ActiveMouseButton), - typeof(MouseButtonType), - typeof(VirtualJoystick), - new PropertyMetadata(MouseButtonType.None)); - - /// - /// 当前激活的鼠标按键依赖属性 | Active mouse button dependency property - /// + DependencyProperty.RegisterReadOnly(nameof(ActiveMouseButton), typeof(MouseButtonType), typeof(VirtualJoystick), + new PropertyMetadata(MouseButtonType.None, OnActiveMouseButtonChanged)); public static readonly DependencyProperty ActiveMouseButtonProperty = ActiveMouseButtonPropertyKey.DependencyProperty; - /// - /// 获取当前激活的鼠标按键(None、Left、Right)| Gets the currently active mouse button (None, Left, Right) - /// + /// 当前激活的鼠标按键 | Currently active mouse button public MouseButtonType ActiveMouseButton { get => (MouseButtonType)GetValue(ActiveMouseButtonProperty); internal set => SetValue(ActiveMouseButtonPropertyKey, value); } - #endregion - - #region 左键四向图标依赖属性 | Left Button Directional Icon Dependency Properties - - /// - /// 左键拖动时上方图标内容依赖属性 | Left button top icon content dependency property - /// - public static readonly DependencyProperty LeftButtonTopIconProperty = - DependencyProperty.Register( - nameof(LeftButtonTopIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置左键拖动时上方图标内容 | Gets or sets the top icon content when left button is dragging - /// - public object? LeftButtonTopIcon + private static void OnActiveMouseButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - get => GetValue(LeftButtonTopIconProperty); - set => SetValue(LeftButtonTopIconProperty, value); - } - - /// - /// 左键拖动时下方图标内容依赖属性 | Left button bottom icon content dependency property - /// - public static readonly DependencyProperty LeftButtonBottomIconProperty = - DependencyProperty.Register( - nameof(LeftButtonBottomIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置左键拖动时下方图标内容 | Gets or sets the bottom icon content when left button is dragging - /// - public object? LeftButtonBottomIcon - { - get => GetValue(LeftButtonBottomIconProperty); - set => SetValue(LeftButtonBottomIconProperty, value); - } - - /// - /// 左键拖动时左方图标内容依赖属性 | Left button left icon content dependency property - /// - public static readonly DependencyProperty LeftButtonLeftIconProperty = - DependencyProperty.Register( - nameof(LeftButtonLeftIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置左键拖动时左方图标内容 | Gets or sets the left icon content when left button is dragging - /// - public object? LeftButtonLeftIcon - { - get => GetValue(LeftButtonLeftIconProperty); - set => SetValue(LeftButtonLeftIconProperty, value); - } - - /// - /// 左键拖动时右方图标内容依赖属性 | Left button right icon content dependency property - /// - public static readonly DependencyProperty LeftButtonRightIconProperty = - DependencyProperty.Register( - nameof(LeftButtonRightIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置左键拖动时右方图标内容 | Gets or sets the right icon content when left button is dragging - /// - public object? LeftButtonRightIcon - { - get => GetValue(LeftButtonRightIconProperty); - set => SetValue(LeftButtonRightIconProperty, value); + if (d is VirtualJoystick joystick) + joystick.UpdateIconVisibility(); } #endregion - #region 右键四向图标依赖属性 | Right Button Directional Icon Dependency Properties + #region 四向图标依赖属性 | Directional Icon Dependency Properties - /// - /// 右键拖动时上方图标内容依赖属性 | Right button top icon content dependency property - /// - public static readonly DependencyProperty RightButtonTopIconProperty = - DependencyProperty.Register( - nameof(RightButtonTopIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); + // 左键图标 | Left button icons + public static readonly DependencyProperty LeftButtonTopIconProperty = DependencyProperty.Register(nameof(LeftButtonTopIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? LeftButtonTopIcon { get => GetValue(LeftButtonTopIconProperty); set => SetValue(LeftButtonTopIconProperty, value); } - /// - /// 获取或设置右键拖动时上方图标内容 | Gets or sets the top icon content when right button is dragging - /// - public object? RightButtonTopIcon - { - get => GetValue(RightButtonTopIconProperty); - set => SetValue(RightButtonTopIconProperty, value); - } + public static readonly DependencyProperty LeftButtonBottomIconProperty = DependencyProperty.Register(nameof(LeftButtonBottomIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? LeftButtonBottomIcon { get => GetValue(LeftButtonBottomIconProperty); set => SetValue(LeftButtonBottomIconProperty, value); } - /// - /// 右键拖动时下方图标内容依赖属性 | Right button bottom icon content dependency property - /// - public static readonly DependencyProperty RightButtonBottomIconProperty = - DependencyProperty.Register( - nameof(RightButtonBottomIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); + public static readonly DependencyProperty LeftButtonLeftIconProperty = DependencyProperty.Register(nameof(LeftButtonLeftIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? LeftButtonLeftIcon { get => GetValue(LeftButtonLeftIconProperty); set => SetValue(LeftButtonLeftIconProperty, value); } - /// - /// 获取或设置右键拖动时下方图标内容 | Gets or sets the bottom icon content when right button is dragging - /// - public object? RightButtonBottomIcon - { - get => GetValue(RightButtonBottomIconProperty); - set => SetValue(RightButtonBottomIconProperty, value); - } + public static readonly DependencyProperty LeftButtonRightIconProperty = DependencyProperty.Register(nameof(LeftButtonRightIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? LeftButtonRightIcon { get => GetValue(LeftButtonRightIconProperty); set => SetValue(LeftButtonRightIconProperty, value); } - /// - /// 右键拖动时左方图标内容依赖属性 | Right button left icon content dependency property - /// - public static readonly DependencyProperty RightButtonLeftIconProperty = - DependencyProperty.Register( - nameof(RightButtonLeftIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); + // 右键图标 | Right button icons + public static readonly DependencyProperty RightButtonTopIconProperty = DependencyProperty.Register(nameof(RightButtonTopIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? RightButtonTopIcon { get => GetValue(RightButtonTopIconProperty); set => SetValue(RightButtonTopIconProperty, value); } - /// - /// 获取或设置右键拖动时左方图标内容 | Gets or sets the left icon content when right button is dragging - /// - public object? RightButtonLeftIcon - { - get => GetValue(RightButtonLeftIconProperty); - set => SetValue(RightButtonLeftIconProperty, value); - } + public static readonly DependencyProperty RightButtonBottomIconProperty = DependencyProperty.Register(nameof(RightButtonBottomIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? RightButtonBottomIcon { get => GetValue(RightButtonBottomIconProperty); set => SetValue(RightButtonBottomIconProperty, value); } - /// - /// 右键拖动时右方图标内容依赖属性 | Right button right icon content dependency property - /// - public static readonly DependencyProperty RightButtonRightIconProperty = - DependencyProperty.Register( - nameof(RightButtonRightIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); + public static readonly DependencyProperty RightButtonLeftIconProperty = DependencyProperty.Register(nameof(RightButtonLeftIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? RightButtonLeftIcon { get => GetValue(RightButtonLeftIconProperty); set => SetValue(RightButtonLeftIconProperty, value); } - /// - /// 获取或设置右键拖动时右方图标内容 | Gets or sets the right icon content when right button is dragging - /// - public object? RightButtonRightIcon - { - get => GetValue(RightButtonRightIconProperty); - set => SetValue(RightButtonRightIconProperty, value); - } + public static readonly DependencyProperty RightButtonRightIconProperty = DependencyProperty.Register(nameof(RightButtonRightIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? RightButtonRightIcon { get => GetValue(RightButtonRightIconProperty); set => SetValue(RightButtonRightIconProperty, value); } - #endregion + // 默认图标 | Default icons + public static readonly DependencyProperty DefaultTopIconProperty = DependencyProperty.Register(nameof(DefaultTopIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? DefaultTopIcon { get => GetValue(DefaultTopIconProperty); set => SetValue(DefaultTopIconProperty, value); } - #region ThumbElement 内部属性 | ThumbElement Internal Property + public static readonly DependencyProperty DefaultBottomIconProperty = DependencyProperty.Register(nameof(DefaultBottomIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? DefaultBottomIcon { get => GetValue(DefaultBottomIconProperty); set => SetValue(DefaultBottomIconProperty, value); } - /// - /// 操控点 UI 元素引用(从模板获取)| Thumb UI element reference (obtained from template) - /// - internal UIElement? ThumbElement { get; private set; } + public static readonly DependencyProperty DefaultLeftIconProperty = DependencyProperty.Register(nameof(DefaultLeftIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? DefaultLeftIcon { get => GetValue(DefaultLeftIconProperty); set => SetValue(DefaultLeftIconProperty, value); } - #endregion - - #region 默认四向图标依赖属性 | Default Directional Icon Dependency Properties - - /// - /// 默认状态上方图标内容依赖属性 | Default top icon content dependency property - /// - public static readonly DependencyProperty DefaultTopIconProperty = - DependencyProperty.Register( - nameof(DefaultTopIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置默认状态上方图标内容 | Gets or sets the top icon content in default state - /// - public object? DefaultTopIcon - { - get => GetValue(DefaultTopIconProperty); - set => SetValue(DefaultTopIconProperty, value); - } - - /// - /// 默认状态下方图标内容依赖属性 | Default bottom icon content dependency property - /// - public static readonly DependencyProperty DefaultBottomIconProperty = - DependencyProperty.Register( - nameof(DefaultBottomIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置默认状态下方图标内容 | Gets or sets the bottom icon content in default state - /// - public object? DefaultBottomIcon - { - get => GetValue(DefaultBottomIconProperty); - set => SetValue(DefaultBottomIconProperty, value); - } - - /// - /// 默认状态左方图标内容依赖属性 | Default left icon content dependency property - /// - public static readonly DependencyProperty DefaultLeftIconProperty = - DependencyProperty.Register( - nameof(DefaultLeftIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置默认状态左方图标内容 | Gets or sets the left icon content in default state - /// - public object? DefaultLeftIcon - { - get => GetValue(DefaultLeftIconProperty); - set => SetValue(DefaultLeftIconProperty, value); - } - - /// - /// 默认状态右方图标内容依赖属性 | Default right icon content dependency property - /// - public static readonly DependencyProperty DefaultRightIconProperty = - DependencyProperty.Register( - nameof(DefaultRightIcon), - typeof(object), - typeof(VirtualJoystick), - new PropertyMetadata(null)); - - /// - /// 获取或设置默认状态右方图标内容 | Gets or sets the right icon content in default state - /// - public object? DefaultRightIcon - { - get => GetValue(DefaultRightIconProperty); - set => SetValue(DefaultRightIconProperty, value); - } - - #endregion - - #region 模板应用 | Template Application - - /// - /// 应用控件模板时查找操控点元素并设置平移变换 | Find thumb element and set up translate transform when template is applied - /// - public override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - // 查找模板中的操控点元素 | Find the thumb element in the template - ThumbElement = GetTemplateChild(PART_Thumb) as UIElement; - - if (ThumbElement != null) - { - // 设置平移变换用于拖拽跟随 | Set translate transform for drag following - ThumbElement.RenderTransform = _thumbTransform; - } - } + public static readonly DependencyProperty DefaultRightIconProperty = DependencyProperty.Register(nameof(DefaultRightIcon), typeof(object), typeof(VirtualJoystick), new PropertyMetadata(null)); + public object? DefaultRightIcon { get => GetValue(DefaultRightIconProperty); set => SetValue(DefaultRightIconProperty, value); } #endregion #region 鼠标交互事件处理 | Mouse Interaction Event Handlers - /// - /// 鼠标左键按下时开始拖拽 | Start dragging when left mouse button is pressed - /// protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); - - // 如果已在拖拽中或控件未启用,忽略 | Ignore if already dragging or control is disabled - if (_isDragging || !IsEnabled) - return; - + if (_isDragging || !IsEnabled) return; StartDrag(MouseButtonType.Left); e.Handled = true; } - /// - /// 鼠标右键按下时开始拖拽 | Start dragging when right mouse button is pressed - /// protected override void OnMouseRightButtonDown(MouseButtonEventArgs e) { base.OnMouseRightButtonDown(e); - - // 如果已在拖拽中或控件未启用,忽略 | Ignore if already dragging or control is disabled - if (_isDragging || !IsEnabled) - return; - + if (_isDragging || !IsEnabled) return; StartDrag(MouseButtonType.Right); e.Handled = true; } - /// - /// 鼠标移动时更新操控点位置和输出值 | Update thumb position and output values when mouse moves - /// protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); - - if (!_isDragging) - return; + if (!_isDragging) return; try { - // 获取鼠标相对于控件的位置 | Get mouse position relative to the control var mousePosition = e.GetPosition(this); - - // 计算相对于中心的偏移量 | Calculate displacement from center var dx = mousePosition.X - _centerPoint.X; var dy = mousePosition.Y - _centerPoint.Y; - // 计算可用半径(取宽高较小值的一半)| Calculate available radius (half of the smaller dimension) + // SingleAxisY 模式下强制水平位移为零,操控点只能上下移动 | Force dx=0 in SingleAxisY mode, thumb moves vertically only + if (JoystickMode == JoystickMode.SingleAxisY) + dx = 0; + var radius = GetRadius(); - if (radius <= 0) - return; + if (radius <= 0) return; - // 将位移限制在圆形区域内 | Clamp displacement within circular area var (clampedDx, clampedDy) = JoystickCalculator.ClampToRadius(dx, dy, radius); - - // 计算归一化输出(含死区映射)| Calculate normalized output (with dead zone mapping) var (outputX, outputY) = JoystickCalculator.CalculateOutput(dx, dy, radius, DeadZone, JoystickMode); - // 更新输出值 | Update output values OutputX = outputX; OutputY = outputY; - - // 更新操控点的平移变换位置(使用限制后的位移)| Update thumb translate transform (using clamped displacement) _thumbTransform.X = clampedDx; _thumbTransform.Y = clampedDy; } catch { - // 异常时重置到中心位置并输出归零 | Reset to center and zero output on exception ResetToCenter(); } - e.Handled = true; } - /// - /// 鼠标左键释放时结束拖拽 | End dragging when left mouse button is released - /// protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); - - // 仅当正在拖拽且激活按键为左键时处理 | Only handle when dragging with left button active - if (!_isDragging || ActiveMouseButton != MouseButtonType.Left) - return; - + if (!_isDragging || ActiveMouseButton != MouseButtonType.Left) return; EndDrag(); e.Handled = true; } - /// - /// 鼠标右键释放时结束拖拽 | End dragging when right mouse button is released - /// protected override void OnMouseRightButtonUp(MouseButtonEventArgs e) { base.OnMouseRightButtonUp(e); - - // 仅当正在拖拽且激活按键为右键时处理 | Only handle when dragging with right button active - if (!_isDragging || ActiveMouseButton != MouseButtonType.Right) - return; - + if (!_isDragging || ActiveMouseButton != MouseButtonType.Right) return; EndDrag(); e.Handled = true; } @@ -564,64 +250,110 @@ namespace XP.Common.Controls #region 拖拽辅助方法 | Drag Helper Methods - /// - /// 开始拖拽操作 | Start drag operation - /// - /// 触发拖拽的鼠标按键类型 | Mouse button type that initiated the drag private void StartDrag(MouseButtonType buttonType) { - // 捕获鼠标以接收控件外的鼠标事件 | Capture mouse to receive events outside the control CaptureMouse(); - - // 设置激活的鼠标按键 | Set the active mouse button ActiveMouseButton = buttonType; - - // 计算控件中心点 | Calculate control center point _centerPoint = new Point(ActualWidth / 2.0, ActualHeight / 2.0); - - // 标记拖拽状态 | Mark dragging state _isDragging = true; } - /// - /// 结束拖拽操作 | End drag operation - /// private void EndDrag() { - // 重置到中心位置并归零输出 | Reset to center and zero output ResetToCenter(); - - // 释放鼠标捕获 | Release mouse capture ReleaseMouseCapture(); } - /// - /// 重置操控点到中心位置,输出归零,清除拖拽状态 | Reset thumb to center, zero output, clear drag state - /// + /// 重置操控点到中心位置 | Reset thumb to center internal void ResetToCenter() { - // 输出值归零 | Zero output values OutputX = 0.0; OutputY = 0.0; - - // 清除激活的鼠标按键 | Clear active mouse button ActiveMouseButton = MouseButtonType.None; - - // 清除拖拽状态 | Clear dragging state _isDragging = false; - - // 重置操控点平移变换到中心 | Reset thumb translate transform to center _thumbTransform.X = 0.0; _thumbTransform.Y = 0.0; } /// - /// 获取可用半径(取宽高较小值的一半)| Get available radius (half of the smaller dimension) + /// 获取可用半径(限制为控件尺寸的 80%)| Get usable radius (limited to 80% of control size) + /// 双轴取宽高最小值的一半,单轴取高度的一半 | DualAxis uses min(W,H)/2, SingleAxisY uses H/2 /// - /// 可用半径 | Available radius private double GetRadius() { - return Math.Min(ActualWidth, ActualHeight) / 2.0; + var raw = JoystickMode == JoystickMode.SingleAxisY + ? ActualHeight / 2.0 + : Math.Min(ActualWidth, ActualHeight) / 2.0; + return raw * 0.8; + } + + #endregion + + #region 图标可见性切换 | Icon Visibility Switching + + /// + /// 根据 ActiveMouseButton 和 JoystickMode 更新图标可见性 | Update icon visibility based on ActiveMouseButton and JoystickMode + /// + private void UpdateIconVisibility() + { + // 查找命名元素 | Find named elements + var defaultTop = FindName("DefaultTopPresenter") as UIElement; + var defaultBottom = FindName("DefaultBottomPresenter") as UIElement; + var defaultLeft = FindName("DefaultLeftPresenter") as UIElement; + var defaultRight = FindName("DefaultRightPresenter") as UIElement; + var leftTop = FindName("LeftTopPresenter") as UIElement; + var leftBottom = FindName("LeftBottomPresenter") as UIElement; + var leftLeft = FindName("LeftLeftPresenter") as UIElement; + var leftRight = FindName("LeftRightPresenter") as UIElement; + var rightTop = FindName("RightTopPresenter") as UIElement; + var rightBottom = FindName("RightBottomPresenter") as UIElement; + var rightLeft = FindName("RightLeftPresenter") as UIElement; + var rightRight = FindName("RightRightPresenter") as UIElement; + var dualBg = FindName("DualAxisBackground") as UIElement; + var singleBg = FindName("SingleAxisBackground") as UIElement; + var hLine = FindName("HorizontalLine") as UIElement; + + if (defaultTop == null) return; // 控件尚未加载 | Control not yet loaded + + // 切换背景形状:双轴=圆形,单轴=腰圆 | Switch background: DualAxis=circle, SingleAxisY=capsule + var isSingleAxis = JoystickMode == JoystickMode.SingleAxisY; + if (dualBg != null) dualBg.Visibility = isSingleAxis ? Visibility.Collapsed : Visibility.Visible; + if (singleBg != null) singleBg.Visibility = isSingleAxis ? Visibility.Visible : Visibility.Collapsed; + if (hLine != null) hLine.Visibility = isSingleAxis ? Visibility.Collapsed : Visibility.Visible; + + // 先全部隐藏 | Hide all first + SetVisibility(Visibility.Collapsed, leftTop, leftBottom, leftLeft, leftRight, rightTop, rightBottom, rightLeft, rightRight); + SetVisibility(Visibility.Visible, defaultTop, defaultBottom, defaultLeft, defaultRight); + + // 根据 ActiveMouseButton 切换 | Switch based on ActiveMouseButton + switch (ActiveMouseButton) + { + case MouseButtonType.Left: + SetVisibility(Visibility.Collapsed, defaultTop, defaultBottom, defaultLeft, defaultRight); + SetVisibility(Visibility.Visible, leftTop, leftBottom, leftLeft, leftRight); + if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x3A, 0x7B, 0xC8)); + break; + case MouseButtonType.Right: + SetVisibility(Visibility.Collapsed, defaultTop, defaultBottom, defaultLeft, defaultRight); + SetVisibility(Visibility.Visible, rightTop, rightBottom, rightLeft, rightRight); + if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x5B, 0xA8, 0x5B)); + break; + default: + if (_thumbElement != null) _thumbElement.Fill = new SolidColorBrush(Color.FromRgb(0x4A, 0x90, 0xD9)); + break; + } + + // SingleAxisY 模式下隐藏左右图标 | Hide left/right icons in SingleAxisY mode + if (isSingleAxis) + { + SetVisibility(Visibility.Collapsed, defaultLeft, defaultRight, leftLeft, leftRight, rightLeft, rightRight); + } + } + + private static void SetVisibility(Visibility visibility, params UIElement?[] elements) + { + foreach (var element in elements) + if (element != null) element.Visibility = visibility; } #endregion diff --git a/XP.Common/Controls/VirtualJoystick.xaml b/XP.Common/Controls/VirtualJoystick.xaml new file mode 100644 index 0000000..afa6d25 --- /dev/null +++ b/XP.Common/Controls/VirtualJoystick.xaml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/XP.Common/XP.Common.csproj b/XP.Common/XP.Common.csproj index 6a45ec0..cea0228 100644 --- a/XP.Common/XP.Common.csproj +++ b/XP.Common/XP.Common.csproj @@ -31,8 +31,7 @@ - - + diff --git a/XP.Hardware.MotionControl/Resources/Resources.en-US.resx b/XP.Hardware.MotionControl/Resources/Resources.en-US.resx index b38b168..c743526 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.en-US.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.en-US.resx @@ -293,12 +293,12 @@ Confirm to filll move matrix? Calibration Value - Enable + Physical Joystick Enable - Save + Save Position - Restore + Restore Position \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Resources/Resources.resx b/XP.Hardware.MotionControl/Resources/Resources.resx index e0b9114..6a8829a 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.resx @@ -347,15 +347,15 @@ DetectorZ → {4:F2}mm 校准自动计算值标签 | Calibration value label - 使能 + 实体操作摇杆使能 使能开关标签 | Enable toggle label - 保存 + 保存当前位置 保存按钮文本 | Save button text - 恢复 + 恢复前一位置 恢复按钮文本 | Restore button text \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx b/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx index 6b40bd2..2bc19e5 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.zh-CN.resx @@ -293,12 +293,12 @@ DetectorZ → {4:F2}mm 校准计算值 - 使能 + 实体操作摇杆使能 - 保存 + 保存当前位置 - 恢复 + 恢复前一位置 \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx b/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx index a888ffb..b7c1687 100644 --- a/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx +++ b/XP.Hardware.MotionControl/Resources/Resources.zh-TW.resx @@ -293,12 +293,12 @@ DetectorZ → {4:F2}mm 校準計算值 - 使能 + 實體操作搖桿使能 - 保存 + 保存當前位置 - 恢復 + 恢復前一位置 \ No newline at end of file diff --git a/XP.Hardware.MotionControl/Views/AxisControlView.xaml b/XP.Hardware.MotionControl/Views/AxisControlView.xaml index 07023a9..9b47949 100644 --- a/XP.Hardware.MotionControl/Views/AxisControlView.xaml +++ b/XP.Hardware.MotionControl/Views/AxisControlView.xaml @@ -9,146 +9,148 @@ xmlns:controls="clr-namespace:XP.Common.Controls;assembly=XP.Common" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" + MinWidth="350" HorizontalAlignment="Stretch" Background="White"> - - - - - + + + + + - - + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - - - + + + - - - - - - - - - - - - - - + + + + - - - + + + + - - - - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +