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">
-
-
-
-
-
+
+
+
+
+
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
+
+
-
-
-
+
+
-
-
-
+
+
-
-
-
+
+
-
-
-
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+