Files
ShrlAlgoToolkit/NeuWPF/NeoUI/Controls/NumericBox.cs
ShrlAlgo 955a01f564 整理
2025-08-20 12:10:35 +08:00

372 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Windows.Input;
namespace NeoUI.Controls;
/// <summary>
/// 用于输入和显示数值的控件。该控件继承自TextBox但增加了对数值范围、小数位数等的控制。
/// </summary>
public class NumericBox : TextBox
{
#region DependencyProperty
private const double VbCurvalue = 0; //当前值
private const double VbMinvalue = double.MinValue; //最小值
private const double VbMaxvalue = double.MaxValue; //最大值
private const int VbDigits = 15; //小数点精度
/// <summary>
///
/// </summary>
public static readonly DependencyProperty CurValueProperty;
/// <summary>
/// 定义NumericBox控件的最小值属性。此依赖属性允许设置和获取控件中可以输入或显示的最小数值。
/// 该属性的默认值为double类型的最小可能值即double.MinValue。
/// </summary>
public static readonly DependencyProperty MinValueProperty;
/// <summary>
/// 代表NumericBox控件中可以设置的最大数值属性。此属性允许用户定义NumericBox实例能够接受的最大值。
/// 默认情况下该属性的值被设置为double类型的最大值即表示在没有特别指定的情况下控件理论上支持所有小于或等于double.MaxValue的数值作为最大值。
/// 可以通过设置此属性来限制用户输入或程序设定的数值不超过某个特定的上限。
/// </summary>
public static readonly DependencyProperty MaxValueProperty;
/// <summary>
/// 表示NumericBox控件中允许显示的小数位数的依赖属性。该属性定义了数值精度用户可以通过设置此属性来控制数字显示时的小数点后位数。
/// 属性值的有效范围是0到15。如果设置的值小于等于0则默认为0若大于15则会被限制为最大值15。
/// </summary>
public static readonly DependencyProperty DigitsProperty;
/// <summary>
/// 获取或设置当前数值框中的值。该属性会根据设定的最小值MinValue、最大值MaxValue和小数位数Digits自动调整输入值确保其处于允许的范围内并且格式正确。
/// 当尝试设置超出范围的值时CurValue 会自动修正为最近的有效边界值;同时,它也会根据 Digits 属性指定的小数位数对数值进行四舍五入处理。
/// </summary>
public double CurValue
{
get => (double)GetValue(CurValueProperty);
set
{
var v = value;
if(value < MinValue)
{
v = MinValue;
}
else if(value > MaxValue)
{
v = MaxValue;
}
v = Math.Round(v, Digits);
SetValue(CurValueProperty, v);
// if do not go into OnCurValueChanged then force update ui
if(v != value)
{
this.Text = v.ToString();
}
}
}
/// <summary>
/// 获取或设置NumericBox控件的最小值。此属性允许定义控件中可以输入或显示的最小数值。
/// 通过设置此属性可以确保用户不能输入低于指定值的数值。默认情况下该属性的值被设定为double类型的最小可能值即double.MinValue。
/// 当尝试将当前值CurValue设置为小于MinValue时系统会自动将CurValue调整到MinValue。
/// </summary>
public double MinValue
{
get => (double)GetValue(MinValueProperty);
set => SetValue(MinValueProperty, value);
}
/// <summary>
/// 代表NumericBox控件中可以设置的最大数值属性。此依赖属性允许用户定义NumericBox实例能够接受的最大值。
/// 默认情况下该属性的值被设置为double类型的最大可能值即double.MaxValue。
/// 通过设置此属性,可以限制用户输入或程序设定的数值不超过某个特定的上限。
/// </summary>
public double MaxValue
{
get => (double)GetValue(MaxValueProperty);
set => SetValue(MaxValueProperty, value);
}
/// <summary>
/// 表示NumericBox控件中允许显示的小数位数的属性。通过设置此属性可以控制数值在显示时小数点后的位数。
/// 属性值的有效范围是0到15。如果设置的值小于等于0则默认为0若大于15则会被限制为最大值15。
/// </summary>
public int Digits
{
get => (int)GetValue(DigitsProperty);
set
{
var digits = value;
if(digits <= 0)
{
digits = 0;
}
if(digits > 15)
{
digits = 15;
}
SetValue(DigitsProperty, value);
}
}
static NumericBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericBox), new FrameworkPropertyMetadata(typeof(NumericBox)));
var metadata = new FrameworkPropertyMetadata(VbCurvalue, OnCurValueChanged);
CurValueProperty = DependencyProperty.Register("CurValue", typeof(double), typeof(NumericBox), metadata);
metadata = new FrameworkPropertyMetadata(VbMinvalue, OnMinValueChanged);
MinValueProperty = DependencyProperty.Register("MinValue", typeof(double), typeof(NumericBox), metadata);
metadata = new FrameworkPropertyMetadata(VbMaxvalue, OnMaxValueChanged);
MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(double), typeof(NumericBox), metadata);
metadata = new FrameworkPropertyMetadata(VbDigits, OnDigitsChanged);
DigitsProperty = DependencyProperty.Register("Digits", typeof(int), typeof(NumericBox), metadata);
}
private static void OnCurValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var value = (double)e.NewValue;
var numericBox = (NumericBox)sender;
numericBox.Text = value.ToString();
}
private static void OnMinValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var minValue = (double)e.NewValue;
var numericBox = (NumericBox)sender;
numericBox.MinValue = minValue;
}
private static void OnMaxValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var maxValue = (double)e.NewValue;
var numericBox = (NumericBox)sender;
numericBox.MaxValue = maxValue;
}
private static void OnDigitsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var digits = (int)e.NewValue;
var numericBox = (NumericBox)sender;
numericBox.CurValue = Math.Round(numericBox.CurValue, digits);
numericBox.MinValue = Math.Round(numericBox.MinValue, digits);
numericBox.MaxValue = Math.Round(numericBox.MaxValue, digits);
}
#endregion
/// <summary>
/// 用于输入和显示数值的控件。该控件继承自TextBox但增加了对数值范围、小数位数等的控制。
/// </summary>
public NumericBox()
{
this.TextChanged += NumericBox_TextChanged;
this.PreviewKeyDown += NumericBox_KeyDown;
this.LostFocus += NumericBox_LostFocus;
DataObject.AddPastingHandler(this, NumericBox_Pasting);
}
void NumericBox_TextChanged(object sender, TextChangedEventArgs e)
{
var numericBox = sender as NumericBox;
if(string.IsNullOrEmpty(numericBox?.Text))
{
return;
}
TrimZeroStart();
if(!Double.TryParse(numericBox?.Text, out var value))
{
return;
}
if(value != this.CurValue)
{
this.CurValue = value;
}
}
void NumericBox_KeyDown(object sender, KeyEventArgs e)
{
var key = e.Key;
if(IsControlKeys(key))
{
return;
}
if(IsDigit(key))
{
return;
}
if(IsSubtract(key)) //-
{
if (sender is not TextBox textBox) return;
var str = textBox.Text;
if(str.Length > 0 && textBox.SelectionStart != 0)
{
e.Handled = true;
}
}
else if(IsDot(key)) //point
{
if(this.Digits > 0)
{
if (sender is not TextBox textBox) return;
var str = textBox.Text;
if(str.Contains('.') || str == "-")
{
e.Handled = true;
}
}
else
{
e.Handled = true;
}
}
else
{
e.Handled = true;
}
}
void NumericBox_LostFocus(object sender, RoutedEventArgs e)
{
var numericBox = sender as NumericBox;
if (!string.IsNullOrEmpty(numericBox?.Text)) return;
if (numericBox != null) numericBox.Text = this.CurValue.ToString();
}
private static void NumericBox_Pasting(object sender, DataObjectPastingEventArgs e) { e.CancelCommand(); }
private static readonly List<Key> ControlKeys =
[
Key.Back,
Key.CapsLock,
Key.Down,
Key.End,
Key.Enter,
Key.Escape,
Key.Home,
Key.Insert,
Key.Left,
Key.PageDown,
Key.PageUp,
Key.Right,
Key.Tab,
Key.Up
];
/// <summary>
/// 判断给定的键是否为控制键。
/// </summary>
/// <param name="key">要检查的键。</param>
/// <returns>如果键是控制键则返回true否则返回false。</returns>
public static bool IsControlKeys(Key key)
{
return ControlKeys.Contains(key);
}
/// <summary>
/// 判断给定的键是否为数字键。
/// </summary>
/// <param name="key">要检查的键。</param>
/// <returns>如果键是数字键则返回true否则返回false。</returns>
public static bool IsDigit(Key key)
{
var shiftKey = (Keyboard.Modifiers & ModifierKeys.Shift) != 0;
bool retVal;
if(key >= Key.D0 && key <= Key.D9 && !shiftKey)
{
retVal = true;
}
else
{
retVal = key >= Key.NumPad0 && key <= Key.NumPad9;
}
return retVal;
}
/// <summary>
/// 判断给定的键是否为小数点键。
/// </summary>
/// <param name="key">要检查的键。</param>
/// <returns>如果键是小数点键则返回true否则返回false。</returns>
public static bool IsDot(Key key)
{
var shiftKey = (Keyboard.Modifiers & ModifierKeys.Shift) != 0;
var flag = false;
if(key == Key.Decimal)
{
flag = true;
}
if(key == Key.OemPeriod && !shiftKey)
{
flag = true;
}
return flag;
}
/// <summary>
/// 判断给定的键是否为减号键。
/// </summary>
/// <param name="key">要检查的键。</param>
/// <returns>如果键是减号键则返回true否则返回false。</returns>
public static bool IsSubtract(Key key)
{
var shiftKey = (Keyboard.Modifiers & ModifierKeys.Shift) != 0;
var flag = false;
if(key == Key.Subtract)
{
flag = true;
}
if(key == Key.OemMinus && !shiftKey)
{
flag = true;
}
return flag;
}
private void TrimZeroStart()
{
if(this.Text.Length == 1)
{
return;
}
var zeroCount = 0;
foreach(var c in this.Text)
{
if(c == '0')
{
zeroCount++;
}
else
{
break;
}
}
if(zeroCount == 0)
{
return;
}
if(this.Text.Contains('.'))
{
if(this.Text[zeroCount] != '.')
{
}
else if(zeroCount > 1)
{
}
}
else if(zeroCount > 0)
{
}
}
}