using System; using System.Windows; using System.Globalization; using System.Windows.Input; using Microsoft.Windows.Controls.Primitives; namespace Microsoft.Windows.Controls { public class NumericUpDown : UpDownBase { #region Members /// /// Flags if the Text and Value properties are in the process of being sync'd /// private bool _isSyncingTextAndValueProperties; #endregion //Members #region Properties #region Minimum public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(decimal), typeof(NumericUpDown), new PropertyMetadata(Decimal.MinValue, OnMinimumPropertyChanged)); public decimal Minimum { get { return (decimal)GetValue(MinimumProperty); } set { SetValue(MinimumProperty, value); } } private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { NumericUpDown nud = d as NumericUpDown; nud.SetValidSpinDirection(); } #endregion Minimum #region Maximum public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(decimal), typeof(NumericUpDown), new PropertyMetadata(Decimal.MaxValue, OnMaximumPropertyChanged)); public decimal Maximum { get { return (decimal)GetValue(MaximumProperty); } set { SetValue(MaximumProperty, value); } } private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { NumericUpDown nud = d as NumericUpDown; nud.SetValidSpinDirection(); } #endregion Maximum #region Increment public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register("Increment", typeof(decimal), typeof(NumericUpDown), new PropertyMetadata(1M)); public decimal Increment { get { return (decimal)GetValue(IncrementProperty); } set { SetValue(IncrementProperty, value); } } #endregion #region FormatString public static readonly DependencyProperty StringFormatProperty = DependencyProperty.Register("FormatString", typeof(string), typeof(NumericUpDown), new PropertyMetadata(string.Empty, OnStringFormatPropertyPropertyChanged)); public string FormatString { get { return (string)GetValue(StringFormatProperty); } set { SetValue(StringFormatProperty, value); } } private static void OnStringFormatPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { NumericUpDown nud = d as NumericUpDown; nud.OnStringFormatChanged(e.OldValue.ToString(), e.NewValue.ToString()); } protected virtual void OnStringFormatChanged(string oldValue, string newValue) { SyncTextAndValueProperties(NumericUpDown.TextProperty, Value); } #endregion //FormatString #region SelectAllOnGotFocus public static readonly DependencyProperty SelectAllOnGotFocusProperty = DependencyProperty.Register("SelectAllOnGotFocus", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(false)); public bool SelectAllOnGotFocus { get { return (bool)GetValue(SelectAllOnGotFocusProperty); } set { SetValue(SelectAllOnGotFocusProperty, value); } } #endregion //SelectAllOnGotFocus #region Value public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(decimal?), typeof(NumericUpDown), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged, OnCoerceValue)); public decimal? Value { get { return (decimal?)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } private static object OnCoerceValue(DependencyObject o, object value) { NumericUpDown numericUpDown = o as NumericUpDown; if (numericUpDown != null) return numericUpDown.OnCoerceValue((decimal?)value); else return value; } protected virtual decimal? OnCoerceValue(decimal? value) { if (value == null) return value; decimal val = value.Value; if (val < Minimum) { return Minimum; } else if (val > Maximum) { return Maximum; } else { return value; } } private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { NumericUpDown numericUpDown = o as NumericUpDown; if (numericUpDown != null) numericUpDown.OnValueChanged((decimal?)e.OldValue, (decimal?)e.NewValue); } protected virtual void OnValueChanged(decimal? oldValue, decimal? newValue) { SetValidSpinDirection(); SyncTextAndValueProperties(NumericUpDown.ValueProperty, newValue); RoutedPropertyChangedEventArgs args = new RoutedPropertyChangedEventArgs(oldValue, newValue); args.RoutedEvent = NumericUpDown.ValueChangedEvent; RaiseEvent(args); } #endregion //Value #endregion #region Constructors static NumericUpDown() { DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown))); } #endregion //Constructors #region Base Class Overrides protected override void OnAccessKey(AccessKeyEventArgs e) { if (TextBox != null) TextBox.Focus(); base.OnAccessKey(e); } public override void OnApplyTemplate() { base.OnApplyTemplate(); SetValidSpinDirection(); if (SelectAllOnGotFocus) { //in order to select all the text we must handle both the keybord (tabbing) and mouse (clicking) events TextBox.GotKeyboardFocus += OnTextBoxGotKeyBoardFocus; TextBox.PreviewMouseLeftButtonDown += OnTextBoxPreviewMouseLeftButtonDown; } } protected override void OnGotFocus(RoutedEventArgs e) { if (TextBox != null) TextBox.Focus(); } protected override void OnIncrement() { if (Value.HasValue) Value += Increment; } protected override void OnDecrement() { if (Value.HasValue) Value -= Increment; } protected override void OnPreviewKeyDown(KeyEventArgs e) { base.OnPreviewKeyDown(e); if (e.Key == Key.Enter) { if (IsEditable) SyncTextAndValueProperties(InputBase.TextProperty, TextBox.Text); } } protected override void OnTextChanged(string previousValue, string currentValue) { SyncTextAndValueProperties(InputBase.TextProperty, currentValue); } #endregion //Base Class Overrides #region Event Handlers private void OnTextBoxGotKeyBoardFocus(object sender, RoutedEventArgs e) { TextBox.SelectAll(); } void OnTextBoxPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (!TextBox.IsKeyboardFocused) { e.Handled = true; TextBox.Focus(); } } #endregion //Event Handlers #region Events public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(NumericUpDown)); public event RoutedPropertyChangedEventHandler ValueChanged { add { AddHandler(ValueChangedEvent, value); } remove { RemoveHandler(ValueChangedEvent, value); } } #endregion //Events #region Methods private decimal? ConvertTextToValue(string text) { decimal? result = null; if (String.IsNullOrEmpty(text)) return result; NumberFormatInfo info = NumberFormatInfo.GetInstance(CultureInfo.CurrentCulture); try { result = FormatString.Contains("P") ? ParsePercent(text, info) : ParseDecimal(text, info); } catch { Text = ConvertValueToText(Value); return Value; } return result; } private string ConvertValueToText(object value) { if (!Value.HasValue) return String.Empty; return Value.Value.ToString(FormatString, CultureInfo.CurrentCulture); } private void SyncTextAndValueProperties(DependencyProperty p, object newValue) { //prevents recursive syncing properties if (_isSyncingTextAndValueProperties) return; _isSyncingTextAndValueProperties = true; //this only occures when the user typed in the value if (InputBase.TextProperty == p) { string text = newValue == null ? String.Empty : newValue.ToString(); SetValue(NumericUpDown.ValueProperty, ConvertTextToValue(text)); } SetValue(InputBase.TextProperty, ConvertValueToText(newValue)); _isSyncingTextAndValueProperties = false; } private static decimal ParseDecimal(string text, NumberFormatInfo info) { return decimal.Parse(text, NumberStyles.Any, info); } private static decimal ParsePercent(string text, NumberFormatInfo info) { text = text.Replace(info.PercentSymbol, null); decimal result = decimal.Parse(text, NumberStyles.Any, info); result = result / 100; return result; } /// /// Sets the valid spin direction based on current value, minimum and maximum. /// private void SetValidSpinDirection() { ValidSpinDirections validDirections = ValidSpinDirections.None; if (Convert.ToDecimal(Value) < Maximum) { validDirections = validDirections | ValidSpinDirections.Increase; } if (Convert.ToDecimal(Value) > Minimum) { validDirections = validDirections | ValidSpinDirections.Decrease; } if (Spinner != null) { Spinner.ValidSpinDirection = validDirections; } } #endregion //Methods } }