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为什么不执行
}
}
}