|
- using BPA.UIControl.Commons.KnownBoxes;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Controls.Primitives;
- using System.Windows.Data;
- using System.Windows.Input;
-
- namespace BPA.UIControl
- {
- /// <summary>
- /// 数值框
- /// </summary>
- [TemplatePart(Name = TextBoxPartName, Type = typeof(TextBox))]
- [TemplatePart(Name = ButtonIncreasePartName, Type = typeof(ButtonBase))]
- [TemplatePart(Name = ButtonDecreasePartName, Type = typeof(ButtonBase))]
- public class NumericBox : Control
- {
- /// <summary>
- /// 文本框名称
- /// </summary>
- public const string TextBoxPartName = "PART_TextBox";
-
- /// <summary>
- /// 增加按钮名称
- /// </summary>
- public const string ButtonIncreasePartName = "PART_IncreaseButton";
-
- /// <summary>
- /// 减少按钮名称
- /// </summary>
- public const string ButtonDecreasePartName = "PART_DecreaseButton";
-
- /// <summary>
- /// 默认 int 类型正则匹配
- /// </summary>
- public const string DefaultIntPattern = "[0-9-]";
-
- /// <summary>
- /// 默认 double 类型正则匹配
- /// </summary>
- public const string DefaultDoublePattern = "[0-9-+Ee\\.]";
-
- private TextBox textBox;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="NumericBox"/> class.
- /// </summary>
- static NumericBox()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericBox), new FrameworkPropertyMetadata(typeof(NumericBox)));
- }
-
- /// <inheritdoc/>
- public override void OnApplyTemplate()
- {
- base.OnApplyTemplate();
-
- if (GetTemplateChild(TextBoxPartName) is TextBox textBox)
- {
- var textBinding = new Binding(nameof(Text));
- textBinding.Source = this;
- textBinding.Mode = BindingMode.TwoWay;
- textBox.SetBinding(TextBox.TextProperty, textBinding);
- textBox.TextChanged += TextBox_TextChanged;
- textBox.PreviewTextInput += TextBox_PreviewTextInput;
- textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
- textBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, null, new CanExecuteRoutedEventHandler(TextBox_CanExecutePaste)));
- this.textBox = textBox;
- }
-
- if (GetTemplateChild(ButtonIncreasePartName) is ButtonBase upButton)
- {
- upButton.Click += IncreaseButton_Click;
- }
-
- if (GetTemplateChild(ButtonDecreasePartName) is ButtonBase downButton)
- {
- downButton.Click += DecreaseButton_Click;
- }
- }
-
- #region events
-
- /// <summary>
- /// 值改变事件
- /// </summary>
- public static readonly RoutedEvent ValueChangedEvent =
- EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<double?>), typeof(NumericBox));
-
- /// <summary>
- /// 值改变事件处理
- /// </summary>
- public event RoutedPropertyChangedEventHandler<double?> ValueChanged
- {
- add { AddHandler(ValueChangedEvent, value); }
- remove { RemoveHandler(ValueChangedEvent, value); }
- }
-
- #endregion events
-
- #region propteries
-
- /// <summary>
- /// 显示文本
- /// </summary>
- public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
- "Text", typeof(string), typeof(NumericBox), new PropertyMetadata(null, OnTextChanged));
-
- /// <summary>
- /// 显示文本
- /// </summary>
- public string Text
- {
- get { return (string)GetValue(TextProperty); }
- set { SetValue(TextProperty, value); }
- }
-
- /// <summary>
- /// 文本格式
- /// </summary>
- public static readonly DependencyProperty TextFormatProperty = DependencyProperty.Register(
- "TextFormat", typeof(string), typeof(NumericBox), new PropertyMetadata(default(string)));
-
- /// <summary>
- /// 文本格式
- /// </summary>
- public string TextFormat
- {
- get { return (string)GetValue(TextFormatProperty); }
- set { SetValue(TextFormatProperty, value); }
- }
-
- /// <summary>
- /// 值
- /// </summary>
- public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
- "Value", typeof(double?), typeof(NumericBox), new FrameworkPropertyMetadata(default(double?), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged));
-
- /// <summary>
- /// 值
- /// </summary>
- public double? Value
- {
- get { return (double?)GetValue(ValueProperty); }
- set { SetValue(ValueProperty, value); }
- }
-
- /// <summary>
- /// 最小值
- /// </summary>
- public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(
- "MinValue", typeof(double), typeof(NumericBox), new PropertyMetadata(double.MinValue));
-
- /// <summary>
- /// 最小值
- /// </summary>
- public double MinValue
- {
- get { return (double)GetValue(MinValueProperty); }
- set { SetValue(MinValueProperty, value); }
- }
-
- /// <summary>
- /// 最大值
- /// </summary>
- public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(
- "MaxValue", typeof(double), typeof(NumericBox), new PropertyMetadata(double.MaxValue));
-
- /// <summary>
- /// 最大值
- /// </summary>
- public double MaxValue
- {
- get { return (double)GetValue(MaxValueProperty); }
- set { SetValue(MaxValueProperty, value); }
- }
-
- /// <summary>
- /// 是否显示增减按钮
- /// </summary>
- public static readonly DependencyProperty ShowButtonProperty = DependencyProperty.Register(
- "ShowButton", typeof(bool), typeof(NumericBox), new PropertyMetadata(BooleanBoxes.FalseBox));
-
- /// <summary>
- /// 是否显示增减按钮
- /// </summary>
- public bool ShowButton
- {
- get { return (bool)GetValue(ShowButtonProperty); }
- set { SetValue(ShowButtonProperty, BooleanBoxes.Box(value)); }
- }
-
- /// <summary>
- /// 最大长度
- /// </summary>
- public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.Register(
- "MaxLength", typeof(int), typeof(NumericBox), new PropertyMetadata(default(int)));
-
- /// <summary>
- /// 最大长度
- /// </summary>
- public int MaxLength
- {
- get { return (int)GetValue(MaxLengthProperty); }
- set { SetValue(MaxLengthProperty, value); }
- }
-
- /// <summary>
- /// 增减间隔
- /// </summary>
- public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
- "Interval", typeof(double), typeof(NumericBox), new PropertyMetadata(default(double)));
-
- /// <summary>
- /// 增减间隔
- /// </summary>
- public double Interval
- {
- get { return (double)GetValue(IntervalProperty); }
- set { SetValue(IntervalProperty, value); }
- }
-
- /// <summary>
- /// 数值类型
- /// </summary>
- public static readonly DependencyProperty NumericTypeProperty = DependencyProperty.Register(
- "NumericType", typeof(NumericType), typeof(NumericBox), new PropertyMetadata(default(NumericType)));
-
- /// <summary>
- /// 数值类型
- /// </summary>
- public NumericType NumericType
- {
- get { return (NumericType)GetValue(NumericTypeProperty); }
- set { SetValue(NumericTypeProperty, value); }
- }
-
- /// <summary>
- /// 数值输入正则匹配
- /// </summary>
- public static readonly DependencyProperty NumericPatternProperty = DependencyProperty.Register(
- "NumericPattern", typeof(string), typeof(NumericBox), new PropertyMetadata(default(string)));
-
- /// <summary>
- /// 数值输入正则匹配
- /// </summary>
- public string NumericPattern
- {
- get { return (string)GetValue(NumericPatternProperty); }
- set { SetValue(NumericPatternProperty, value); }
- }
-
- /// <summary>
- /// 只读
- /// </summary>
- public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
- "IsReadOnly", typeof(bool), typeof(NumericBox), new PropertyMetadata(BooleanBoxes.FalseBox));
-
- /// <summary>
- /// 只读
- /// </summary>
- public bool IsReadOnly
- {
- get { return (bool)GetValue(IsReadOnlyProperty); }
- set { SetValue(IsReadOnlyProperty, BooleanBoxes.Box(value)); }
- }
-
- #endregion propteries
-
- #region methods
-
- private static double GetCalculatedValue(NumericBox numberBox, double value)
- {
- if (value > numberBox.MaxValue)
- {
- return numberBox.MaxValue;
- }
- else if (value < numberBox.MinValue)
- {
- return numberBox.MinValue;
- }
- else
- {
- return value;
- }
- }
-
- private void GetIntervalAndMin(out double interval, out double min)
- {
- interval = NumericType == NumericType.Double ? Interval : Math.Round(Interval);
- min = MinValue == double.MinValue ? 0 : MinValue;
- }
-
- private void IncreaseButton_Click(object sender, RoutedEventArgs e)
- {
- GetIntervalAndMin(out double interval, out double min);
- Value = GetCalculatedValue(this, Value == null ? min + interval : Value.GetValueOrDefault() + interval);
- textBox.Focus();
- }
-
- private void DecreaseButton_Click(object sender, RoutedEventArgs e)
- {
- GetIntervalAndMin(out double interval, out double min);
- Value = GetCalculatedValue(this, Value == null ? min : Value.GetValueOrDefault() - interval);
- textBox.Focus();
- }
-
- private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var numberBox = d as NumericBox;
-
- if (double.TryParse(numberBox.Text, out double value))
- {
- var newValue = GetCalculatedValue(numberBox, value);
- if (numberBox.Value != newValue)
- {
- if (numberBox.IsLoaded)
- {
- numberBox.Value = newValue;
- }
- else
- {
- numberBox.Loaded += NumberBox_Loaded;
- }
- }
- }
- else if (string.IsNullOrEmpty(numberBox.Text))
- {
- if (InputBoxHelper.GetIsClearable(numberBox))
- {
- numberBox.Value = null;
- }
- else
- {
- if (numberBox.Value.HasValue)
- {
- numberBox.Text = numberBox.Value.Value.ToString(numberBox.TextFormat);
- }
- }
- }
-
- if (!numberBox.Value.HasValue)
- {
- numberBox.Text = null;
- }
- else
- {
- numberBox.Text = numberBox.Value.Value.ToString(numberBox.TextFormat);
- }
- }
-
- private static void NumberBox_Loaded(object sender, RoutedEventArgs e)
- {
- var numberBox = sender as NumericBox;
- if (double.TryParse(numberBox.Text, out double value))
- {
- var newValue = GetCalculatedValue(numberBox, value);
- if (numberBox.Value != newValue)
- {
- numberBox.Value = newValue;
- }
- }
-
- numberBox.Loaded -= NumberBox_Loaded;
- }
-
- private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- var numberBox = d as NumericBox;
- numberBox.Text = numberBox.Value?.ToString(numberBox.TextFormat);
- numberBox.textBox?.Select(numberBox.textBox.Text.Length, 1);
-
- var args = new RoutedPropertyChangedEventArgs<double?>((double?)e.OldValue, (double?)e.NewValue);
- args.RoutedEvent = NumericBox.ValueChangedEvent;
- numberBox.RaiseEvent(args);
- }
-
- private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
- {
- // 限制与数值无关输入
- string pattern = string.IsNullOrEmpty(NumericPattern) ? (NumericType == NumericType.Int ? DefaultIntPattern : DefaultDoublePattern) : NumericPattern;
- var regex = new Regex(pattern);
- e.Handled = !regex.IsMatch(e.Text);
- }
-
- private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
- {
- // 限制空格输入
- if (e.Key == Key.Space)
- {
- e.Handled = true;
- }
-
- if (e.Key == Key.Enter)
- {
- if (textBox.IsFocused)
- {
- this.Focus();
- }
- }
- }
-
- private void TextBox_CanExecutePaste(object sender, CanExecuteRoutedEventArgs e)
- {
- // 限制粘贴
- e.CanExecute = false;
- e.Handled = true;
- }
-
- private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
- {
- var textBox = sender as TextBox;
-
- if (double.TryParse(textBox.Text, out double value))
- {
- if (value < MinValue || value > MaxValue)
- {
- var newValue = GetCalculatedValue(this, value);
- if (Value != newValue)
- {
- Value = newValue;
- }
- else
- {
- textBox.Text = newValue.ToString(TextFormat);
- textBox.Select(textBox.Text.Length, 0);
- }
- }
- }
- }
- #endregion methods
- }
- }
|