using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TeamAAS_VP.Behaviors
{
///
/// 为 `MahApps.Metro.Controls.NumericUpDown` 提供用户输入检测行为。
/// 当控件值由用户交互(鼠标、键盘或文本输入)更改时,触发绑定的命令并传递 (CommandParameter, previousValue, currentValue)。
///
public class NumericUpDownUserInputBehavior : Behavior
{
///
/// 依赖属性:用户输入触发时要执行的命令。
/// 命令接收一个 Tuple<object, double?, double?> 参数,分别是 (CommandParameter, previousValue, currentValue)。
///
public static readonly DependencyProperty UserInputCommandProperty =
DependencyProperty.Register(
nameof(UserInputCommand),
typeof(ICommand),
typeof(NumericUpDownUserInputBehavior));
///
/// 要在用户输入时执行的命令。
///
public ICommand UserInputCommand
{
get => (ICommand)GetValue(UserInputCommandProperty);
set => SetValue(UserInputCommandProperty, value);
}
///
/// 依赖属性:传递给命令的参数(作为命令的第一个项,最终传递给命令的是 Tuple<object, double?, double?>)。
///
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register(
nameof(CommandParameter),
typeof(object),
typeof(NumericUpDownUserInputBehavior));
///
/// 命令参数,最终与 previousValue 和 currentValue 一起封装成 Tuple 传递给命令。
///
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
// 标记当前操作是否为用户交互触发
private bool _isUserInteraction = false;
// 保存上一次值(用于在值变化时向命令提供前后对比)
private double? _previousValue;
///
/// 行为附加时订阅控件的相关事件用于检测用户交互与值变化。
///
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseDown += OnPreviewMouseDown;
AssociatedObject.PreviewKeyDown += OnPreviewKeyDown;
AssociatedObject.ValueChanged += OnValueChanged;
AssociatedObject.LostFocus += OnLostFocus;
AssociatedObject.GotFocus += OnGotFocus;
}
///
/// 行为分离时取消订阅事件,防止内存泄漏。
///
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewMouseDown -= OnPreviewMouseDown;
AssociatedObject.PreviewKeyDown -= OnPreviewKeyDown;
AssociatedObject.ValueChanged -= OnValueChanged;
AssociatedObject.LostFocus -= OnLostFocus;
AssociatedObject.GotFocus -= OnGotFocus;
}
///
/// 当控件获得焦点时,标记为用户交互并记录当前值。
/// 这有助于检测通过文本输入直接修改值的场景。
///
private void OnGotFocus(object sender, RoutedEventArgs e)
{
_isUserInteraction = true;
_previousValue = AssociatedObject.Value;
// 如果需要按当前语言设置文本框语言,可在此处获取应用级服务并设置 PART_TextBox 的 Language。
//var management = App.Host.Services.GetRequiredService();
//var partTextBox = AssociatedObject.Template.FindName("PART_TextBox", AssociatedObject) as TextBox;
//partTextBox.Language = System.Windows.Markup.XmlLanguage.GetLanguage(management.CurrentLanguage);
}
///
/// 在鼠标按下时标记为用户交互并记录当前值(例如点击上下箭头)。
///
private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
_isUserInteraction = true;
_previousValue = AssociatedObject.Value;
}
///
/// 在键盘按下时检测常见的用户输入按键(上下箭头、数字、退格、删除等),以标记为用户交互并记录当前值。
///
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
{
// 检测键盘输入(上下箭头、数字键、删除、退格等)
if (e.Key == Key.Up || e.Key == Key.Down ||
e.Key == Key.Back || e.Key == Key.Delete ||
(e.Key >= Key.D0 && e.Key <= Key.D9) ||
(e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9))
{
_isUserInteraction = true;
_previousValue = AssociatedObject.Value;
}
}
///
/// 值变化时触发:若是用户交互导致的变化且命令可执行,则构造参数并执行命令。
/// 同时更新保存的前一个值以便连续变化时能够正确传递历史值。
///
private void OnValueChanged(object sender, RoutedPropertyChangedEventArgs e)
{
if (_isUserInteraction && UserInputCommand?.CanExecute(CommandParameter) == true)
{
var parameter = new Tuple