using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace FWindSoft.Wpf.Controls { /// /// 输入文本生效方式 /// public enum InputTextForceType { /// /// 实时 /// Time, /// /// 按键 /// KeyDown } [TemplatePart(Name = PART_TextBox, Type = typeof(TextBox))] [TemplatePart(Name = PART_Up, Type = typeof(Button))] [TemplatePart(Name = PART_Down, Type = typeof(Button))] public class IntegerSpinner:Control { static IntegerSpinner() { DefaultStyleKeyProperty.OverrideMetadata(typeof(IntegerSpinner), new FrameworkPropertyMetadata(typeof(IntegerSpinner))); } public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(IntegerSpinner), new FrameworkPropertyMetadata(false)); public bool IsReadOnly { get { return (bool)GetValue(IsReadOnlyProperty); } set { SetValue(IsReadOnlyProperty, value); } } #region 最大值最小值限定相关 public static readonly RoutedEvent ValueChangedEvent=EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(IntegerSpinner)); public static readonly DependencyProperty MinimumProperty=DependencyProperty.Register("Minimum", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(1, new PropertyChangedCallback(OnMinimumChanged))); public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(100, new PropertyChangedCallback(OnMaximumChanged), new CoerceValueCallback(CoerceMaximum))); public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(OnValueChanged), new CoerceValueCallback(ConstrainToRange))); public static readonly DependencyProperty StepProperty = DependencyProperty.Register("Step", typeof(int), typeof(IntegerSpinner), new FrameworkPropertyMetadata(1)); public event RoutedPropertyChangedEventHandler ValueChanged { add { base.AddHandler(ValueChangedEvent, value); } remove { base.RemoveHandler(ValueChangedEvent, value); } } public int Minimum { get { return (int)base.GetValue(MinimumProperty); } set { base.SetValue(MinimumProperty, value); } } public int Maximum { get { return (int)base.GetValue(MaximumProperty); } set { base.SetValue(MaximumProperty, value); } } public int Value { get { return (int)base.GetValue(ValueProperty); } set { base.SetValue(ValueProperty, value); } } public int Step { get { return (int)base.GetValue(StepProperty); } set { base.SetValue(StepProperty, value); } } #region 最小值处理 private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { IntegerSpinner rangeBase = (IntegerSpinner)d; rangeBase.CoerceValue(MaximumProperty); rangeBase.CoerceValue(ValueProperty); rangeBase.OnMinimumChanged((int)e.OldValue, (int)e.NewValue); } protected virtual void OnMinimumChanged(int oldMinimum, int newMinimum) { } #endregion #region 最大值处理 private static object CoerceMaximum(DependencyObject d, object value) { IntegerSpinner rangeBase = (IntegerSpinner)d; int minimum = rangeBase.Minimum; if ((int)value < minimum) { return minimum; } return value; } private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { IntegerSpinner rangeBase = (IntegerSpinner)d; rangeBase.CoerceValue(ValueProperty); rangeBase.OnMaximumChanged((int)e.OldValue, (int)e.NewValue); } protected virtual void OnMaximumChanged(int oldMaximum, int newMaximum) { } #endregion #region 值处理 internal static object ConstrainToRange(DependencyObject d, object value) { IntegerSpinner rangeBase = (IntegerSpinner)d; int minimum = rangeBase.Minimum; int num = (int)value; if (num < minimum) { //加入这个显示,可以强制的更新到绑定的数据源,但是会执行两次,这样保证了Value值,最后的正确性 //但失去焦点,同样会达成这种效果 //rangeBase.Value = minimum; return minimum; } int maximum = rangeBase.Maximum; if (num > maximum) { // //rangeBase.Value = maximum; return maximum; } return value; } private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { IntegerSpinner rangeBase = (IntegerSpinner)d; rangeBase.OnValueChanged((int)e.OldValue, (int)e.NewValue); } protected virtual void OnValueChanged(int oldValue, int newValue) { ValueToText(newValue);//value值变换更新Text值 base.RaiseEvent(new RoutedPropertyChangedEventArgs(oldValue, newValue) { RoutedEvent = IntegerSpinner.ValueChangedEvent }); } #endregion #endregion #region 临时值存储相关 protected TextBox TextBox { get; private set; } protected Button UpButton { get; private set; } protected Button DownButton { get; private set; } public static readonly DependencyProperty ForceTypeProperty = DependencyProperty.Register("ForceType", typeof(InputTextForceType), typeof(IntegerSpinner), new FrameworkPropertyMetadata(InputTextForceType.Time)); public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(IntegerSpinner), new FrameworkPropertyMetadata(null, Text_Changed)); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty,value); } } /// /// 输入文本生效类型 /// public InputTextForceType ForceType { get { return (InputTextForceType)GetValue(ForceTypeProperty); } set { SetValue(ForceTypeProperty, value); } } private bool m_OutChanged=true;//外部赋值引起的Text变化 private bool m_UiChanged = false;//Ui引起的Text值发生变化 public static void Text_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { IntegerSpinner spinner = d as IntegerSpinner; if (spinner == null) return; spinner.TextChanged(e); } private void TextChanged(DependencyPropertyChangedEventArgs e) { //如果是内部修改直接退出 //var test = this.IsInitialized; if (!m_OutChanged) return; var newValue = (string) e.NewValue; if (ForceType == InputTextForceType.KeyDown) { return; } if (!m_UiChanged) { //外部修改,将Text转换成Value TextToValue(newValue); //通过约束的Value,返回TextBox ValueToText(this.Value); SetTextBoxText(TextBox, this); } else { //不回写TextBox,不强制计算 TextToValue(newValue); } } #region Text和value交互 /* 1、Text 的值外接赋值,需要强制刷新界面TextBox的值 2、存在将Value转换成Text的函数 3、Text只与Value发生交互 */ private void ValueToText(int value) { m_OutChanged = false; try { this.Text = value.ToString(); } finally { m_OutChanged = true; } } /// /// 转换成Value /// /// /// 是否给Value赋值 private void TextToValue(string text) { if (string.IsNullOrEmpty(text)) return ; int result = 0; if (Int32.TryParse(text, out result)) { this.Value = result; } } #region Text和TextBox交互 private bool m_UseTextBoxChangeEvent = true; private void SetTextBoxText(TextBox textBox, IntegerSpinner spinner) { try { m_UseTextBoxChangeEvent = false; if (textBox == null) return; textBox.Text = spinner.Text; } finally { m_UseTextBoxChangeEvent = true; } } private void GetTextBoxText(TextBox textBox, IntegerSpinner spinner) { if (textBox == null) return; try { m_UiChanged = true; spinner.Text = textBox.Text; } finally { m_UiChanged = false; } } #endregion #endregion #endregion #region 键盘控制相关 public const string PART_TextBox = "PART_TextBox"; public const string PART_Up = "PART_Up"; public const string PART_Down = "PART_Down"; protected override void OnAccessKey(AccessKeyEventArgs e) { if (TextBox != null) TextBox.Focus(); base.OnAccessKey(e); } #region 子空间事件相关 public override void OnApplyTemplate() { base.OnApplyTemplate(); Detaching(); TextBox = GetTemplateChild(PART_TextBox) as TextBox; UpButton = GetTemplateChild(PART_Up) as Button; DownButton = GetTemplateChild(PART_Down) as Button; Attached(); } private void Attached() { if (TextBox != null) { SetTextBoxText(TextBox, this); TextBox.TextChanged += new TextChangedEventHandler(TextBox_TextChanged); } if (UpButton != null) { UpButton.Click += UpButton_Click; } if (DownButton != null) { DownButton.Click += DownButton_Click; } } private void Detaching() { if (TextBox != null) { TextBox.TextChanged -= new TextChangedEventHandler(TextBox_TextChanged); } if (UpButton != null) { UpButton.Click -= UpButton_Click; } if (DownButton != null) { DownButton.Click-= DownButton_Click; } } private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { if (!m_UseTextBoxChangeEvent) return; GetTextBoxText((TextBox)sender, this); } private void DownButton_Click(object sender, RoutedEventArgs e) { this.Value = this.Value - Step; SetTextBoxText(TextBox, this); } private void UpButton_Click(object sender, RoutedEventArgs e) { this.Value = this.Value + Step; SetTextBoxText(TextBox, this); } #endregion //输入生效模式为KeyDown时,当按下enter,或控件失去焦点,则执行强制提交。这是才修改Value值 //Time时,是实时修改value值 protected override void OnKeyDown(KeyEventArgs e) { switch (e.Key) { case Key.Enter: { this.CommitInput(); if (TextBox != null) TextBox.Select((this.Text ?? string.Empty).Length, 0); break; } } } public void CommitInput() { TextToValue(this.Text); //通过约束的Value,返回TextBox ValueToText(this.Value); SetTextBoxText(TextBox, this); } #endregion public IntegerSpinner() { this.LostFocus += IntegerSpinner_LostFocus; this.Loaded += IntegerSpinner_Loaded; } private void IntegerSpinner_Loaded(object sender, RoutedEventArgs e) { this.Loaded -= IntegerSpinner_Loaded; this.CommitInput(); } private void IntegerSpinner_LostFocus(object sender, RoutedEventArgs e) { //点击按钮也会发生焦点变化,这个暂时不需要过滤 this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() => { CommitInput(); })); } protected override void OnLostFocus(RoutedEventArgs e) { //不知道重写Overide为什么不执行 } } }